From 3d2cbcef6982af82f57aaa9e359edead2419a0d8 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Mon, 11 Jan 2021 21:24:12 -0800 Subject: [PATCH 1/9] Update the ICommandPredictor interface with API changes --- .../CommandPrediction/CommandPrediction.cs | 72 +++++++++++++++---- .../CommandPrediction/ICommandPredictor.cs | 31 +++++--- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs index 30deb1b3093..2c2e83c6efb 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs @@ -27,15 +27,21 @@ public sealed class PredictionResult /// public string Name { get; } + /// + /// Gets the name of the predictor. + /// + public string Session { get; } + /// /// Gets the suggestions. /// public IReadOnlyList Suggestions { get; } - internal PredictionResult(Guid id, string name, List suggestions) + internal PredictionResult(Guid id, string name, string session, List suggestions) { Id = id; Name = name; + Session = session; Suggestions = suggestions; } } @@ -48,22 +54,24 @@ public static class CommandPrediction /// /// Collect the predictive suggestions from registered predictors using the default timeout. /// + /// Represents the client that initiates the call. /// The object from parsing the current command line input. /// The objects from parsing the current command line input. /// A list of objects. - public static Task?> PredictInput(Ast ast, Token[] astTokens) + public static Task?> PredictInput(string client, Ast ast, Token[] astTokens) { - return PredictInput(ast, astTokens, millisecondsTimeout: 20); + return PredictInput(client, ast, astTokens, millisecondsTimeout: 20); } /// /// Collect the predictive suggestions from registered predictors using the specified timeout. /// + /// Represents the client that initiates the call. /// The object from parsing the current command line input. /// The objects from parsing the current command line input. /// The milliseconds to timeout. /// A list of objects. - public static async Task?> PredictInput(Ast ast, Token[] astTokens, int millisecondsTimeout) + public static async Task?> PredictInput(string client, Ast ast, Token[] astTokens, int millisecondsTimeout) { Requires.Condition(millisecondsTimeout > 0, nameof(millisecondsTimeout)); @@ -85,8 +93,10 @@ public static class CommandPrediction state => { var predictor = (ICommandPredictor)state!; - List? texts = predictor.GetSuggestion(context, cancellationSource.Token); - return texts?.Count > 0 ? new PredictionResult(predictor.Id, predictor.Name, texts) : null; + var (session, list) = predictor.GetSuggestion(client, context, cancellationSource.Token); + return session != null && list?.Count > 0 + ? new PredictionResult(predictor.Id, predictor.Name, session, list) + : null; }, predictor, cancellationSource.Token, @@ -99,7 +109,7 @@ await Task.WhenAny( Task.Delay(millisecondsTimeout, cancellationSource.Token)).ConfigureAwait(false); cancellationSource.Cancel(); - var results = new List(predictors.Count); + var resultList = new List(predictors.Count); foreach (Task task in tasks) { if (task.IsCompletedSuccessfully) @@ -107,19 +117,20 @@ await Task.WhenAny( PredictionResult? result = task.Result; if (result != null) { - results.Add(result); + resultList.Add(result); } } } - return results; + return resultList; } /// /// Allow registered predictors to do early processing when a command line is accepted. /// + /// Represents the client that initiates the call. /// History command lines provided as references for prediction. - public static void OnCommandLineAccepted(IReadOnlyList history) + public static void OnCommandLineAccepted(string client, IReadOnlyList history) { Requires.NotNull(history, nameof(history)); @@ -134,7 +145,38 @@ public static void OnCommandLineAccepted(IReadOnlyList history) if (predictor.SupportEarlyProcessing) { ThreadPool.QueueUserWorkItem( - state => state.StartEarlyProcessing(history), + state => state.StartEarlyProcessing(client, history), + predictor, + preferLocal: false); + } + } + } + + /// + /// Send feedback to a predictor when one or more suggestions from it were displayed to the user. + /// + /// The identifier of the predictor whose prediction result was accepted. + /// The mini-session where the displayed suggestions came from. + /// + /// When the value is > 0, it's the number of displayed suggestions from the list returned in , starting from the index 0. + /// When the value is <= 0, it means a single suggestion from the list got displayed, and the index is the absolute value. + /// + public static void OnSuggestionDisplayed(Guid predictorId, string session, int countOrIndex) + { + Requires.NotNullOrEmpty(session, nameof(session)); + + var predictors = SubsystemManager.GetSubsystems(); + if (predictors.Count == 0) + { + return; + } + + foreach (ICommandPredictor predictor in predictors) + { + if (predictor.AcceptFeedback && predictor.Id == predictorId) + { + ThreadPool.QueueUserWorkItem( + state => state.OnSuggestionDisplayed(session, countOrIndex), predictor, preferLocal: false); } @@ -142,12 +184,14 @@ public static void OnCommandLineAccepted(IReadOnlyList history) } /// - /// Send feedback to predictors about their last suggestions. + /// Send feedback to a predictor when a suggestion from it was accepted. /// /// The identifier of the predictor whose prediction result was accepted. + /// The mini-session where the accepted suggestion came from. /// The accepted suggestion text. - public static void OnSuggestionAccepted(Guid predictorId, string suggestionText) + public static void OnSuggestionAccepted(Guid predictorId, string session, string suggestionText) { + Requires.NotNullOrEmpty(session, nameof(session)); Requires.NotNullOrEmpty(suggestionText, nameof(suggestionText)); var predictors = SubsystemManager.GetSubsystems(); @@ -161,7 +205,7 @@ public static void OnSuggestionAccepted(Guid predictorId, string suggestionText) if (predictor.AcceptFeedback && predictor.Id == predictorId) { ThreadPool.QueueUserWorkItem( - state => state.OnSuggestionAccepted(suggestionText), + state => state.OnSuggestionAccepted(session, suggestionText), predictor, preferLocal: false); } diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs index 0edd768cf89..48e864cc305 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs @@ -40,22 +40,35 @@ public interface ICommandPredictor : ISubsystem /// A command line was accepted to execute. /// The predictor can start processing early as needed with the latest history. /// + /// Represents the client that initiates the call. /// History command lines provided as references for prediction. - void StartEarlyProcessing(IReadOnlyList history); + void StartEarlyProcessing(string clientId, IReadOnlyList history); /// - /// The suggestion given by the predictor was accepted. + /// Get the predictive suggestions. It indicates the start of a suggestion rendering session. /// - /// The accepted suggestion text. - void OnSuggestionAccepted(string acceptedSuggestion); + /// Represents the client that initiates the call. + /// The object to be used for prediction. + /// The cancellation token to cancel the prediction. + /// A value tuple consist of a session id, and a list of predictive suggestions. + (string? Session, List? List) GetSuggestion(string clientId, PredictionContext context, CancellationToken cancellationToken); /// - /// Get the predictive suggestions. + /// One or more suggestions provided by the predictor were displayed to the user. /// - /// The object to be used for prediction. - /// The cancellation token to cancel the prediction. - /// A list of predictive suggestions. - List? GetSuggestion(PredictionContext context, CancellationToken cancellationToken); + /// The mini-session where the displayed suggestions came from. + /// + /// When the value is > 0, it's the number of displayed suggestions from the list returned in , starting from the index 0. + /// When the value is <= 0, it means a single suggestion from the list got displayed, and the index is the absolute value. + /// + void OnSuggestionDisplayed(string session, int countOrIndex); + + /// + /// The suggestion provided by the predictor was accepted. + /// + /// Represents the mini-session where the accepted suggestion came from. + /// The accepted suggestion text. + void OnSuggestionAccepted(string session, string acceptedSuggestion); } /// From 589666e77110d7d578b2bfa63e9d613004208aa6 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 12 Jan 2021 13:17:17 -0800 Subject: [PATCH 2/9] Use 'uint' type for the mini-session id --- .../CommandPrediction/CommandPrediction.cs | 19 +++++------ .../CommandPrediction/ICommandPredictor.cs | 33 ++++++++++++++++--- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs index 2c2e83c6efb..bbc0690073a 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs @@ -28,16 +28,16 @@ public sealed class PredictionResult public string Name { get; } /// - /// Gets the name of the predictor. + /// Gets the mini-session id that represents a specific invocation to the API of the predictor. /// - public string Session { get; } + public uint Session { get; } /// /// Gets the suggestions. /// public IReadOnlyList Suggestions { get; } - internal PredictionResult(Guid id, string name, string session, List suggestions) + internal PredictionResult(Guid id, string name, uint session, List suggestions) { Id = id; Name = name; @@ -93,9 +93,9 @@ public static class CommandPrediction state => { var predictor = (ICommandPredictor)state!; - var (session, list) = predictor.GetSuggestion(client, context, cancellationSource.Token); - return session != null && list?.Count > 0 - ? new PredictionResult(predictor.Id, predictor.Name, session, list) + SuggestionPackage pkg = predictor.GetSuggestion(client, context, cancellationSource.Token); + return pkg.Session.HasValue && pkg.SuggestionEntries?.Count > 0 + ? new PredictionResult(predictor.Id, predictor.Name, pkg.Session.Value, pkg.SuggestionEntries) : null; }, predictor, @@ -161,10 +161,8 @@ public static void OnCommandLineAccepted(string client, IReadOnlyList hi /// When the value is > 0, it's the number of displayed suggestions from the list returned in , starting from the index 0. /// When the value is <= 0, it means a single suggestion from the list got displayed, and the index is the absolute value. /// - public static void OnSuggestionDisplayed(Guid predictorId, string session, int countOrIndex) + public static void OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex) { - Requires.NotNullOrEmpty(session, nameof(session)); - var predictors = SubsystemManager.GetSubsystems(); if (predictors.Count == 0) { @@ -189,9 +187,8 @@ public static void OnSuggestionDisplayed(Guid predictorId, string session, int c /// The identifier of the predictor whose prediction result was accepted. /// The mini-session where the accepted suggestion came from. /// The accepted suggestion text. - public static void OnSuggestionAccepted(Guid predictorId, string session, string suggestionText) + public static void OnSuggestionAccepted(Guid predictorId, uint session, string suggestionText) { - Requires.NotNullOrEmpty(session, nameof(session)); Requires.NotNullOrEmpty(suggestionText, nameof(suggestionText)); var predictors = SubsystemManager.GetSubsystems(); diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs index 48e864cc305..0cf72fd4ebc 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs @@ -50,8 +50,8 @@ public interface ICommandPredictor : ISubsystem /// Represents the client that initiates the call. /// The object to be used for prediction. /// The cancellation token to cancel the prediction. - /// A value tuple consist of a session id, and a list of predictive suggestions. - (string? Session, List? List) GetSuggestion(string clientId, PredictionContext context, CancellationToken cancellationToken); + /// An instance of . + SuggestionPackage GetSuggestion(string clientId, PredictionContext context, CancellationToken cancellationToken); /// /// One or more suggestions provided by the predictor were displayed to the user. @@ -61,14 +61,14 @@ public interface ICommandPredictor : ISubsystem /// When the value is > 0, it's the number of displayed suggestions from the list returned in , starting from the index 0. /// When the value is <= 0, it means a single suggestion from the list got displayed, and the index is the absolute value. /// - void OnSuggestionDisplayed(string session, int countOrIndex); + void OnSuggestionDisplayed(uint session, int countOrIndex); /// /// The suggestion provided by the predictor was accepted. /// /// Represents the mini-session where the accepted suggestion came from. /// The accepted suggestion text. - void OnSuggestionAccepted(string session, string acceptedSuggestion); + void OnSuggestionAccepted(uint session, string acceptedSuggestion); } /// @@ -173,4 +173,29 @@ public PredictiveSuggestion(string suggestion, string? toolTip) ToolTip = toolTip; } } + + /// + /// A package returned from . + /// + public struct SuggestionPackage + { + /// + /// The mini-session that represents a specific invocation to . + /// + public uint? Session { get; } + + /// + /// Suggestion entries returned from that mini-session. + /// + public List? SuggestionEntries { get; } + + /// + /// Initializes a new instance of the struct. + /// + public SuggestionPackage(uint session, List suggestionEntries) + { + Session = session; + SuggestionEntries = suggestionEntries; + } + } } From 203683e96bcae0685e298cf873760e51e595aabd Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 12 Jan 2021 21:26:08 -0800 Subject: [PATCH 3/9] Minor update --- .../engine/Subsystem/CommandPrediction/CommandPrediction.cs | 4 +--- .../engine/Subsystem/CommandPrediction/ICommandPredictor.cs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs index bbc0690073a..385839df847 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs @@ -94,9 +94,7 @@ public static class CommandPrediction { var predictor = (ICommandPredictor)state!; SuggestionPackage pkg = predictor.GetSuggestion(client, context, cancellationSource.Token); - return pkg.Session.HasValue && pkg.SuggestionEntries?.Count > 0 - ? new PredictionResult(predictor.Id, predictor.Name, pkg.Session.Value, pkg.SuggestionEntries) - : null; + return pkg.SuggestionEntries?.Count > 0 ? new PredictionResult(predictor.Id, predictor.Name, pkg.Session, pkg.SuggestionEntries) : null; }, predictor, cancellationSource.Token, diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs index 0cf72fd4ebc..ee88dd5c507 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs @@ -182,7 +182,7 @@ public struct SuggestionPackage /// /// The mini-session that represents a specific invocation to . /// - public uint? Session { get; } + public uint Session { get; } /// /// Suggestion entries returned from that mini-session. From 4eb7aac1cce4589a503aa9c6f84f666be75e5008 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 Jan 2021 09:07:41 -0800 Subject: [PATCH 4/9] Fix script that create new NuGet package --- tools/packaging/packaging.psm1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index aaaee8688d0..3d996f338aa 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -2367,6 +2367,11 @@ function CleanupGeneratedSourceCode Pattern = "[System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.NullableContextAttribute((byte)2)]" Replacement = "/* [System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.NullableContextAttribute((byte)2)] */ " }, + @{ + ApplyTo = @("System.Management.Automation") + Pattern = "[System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.IsReadOnlyAttribute]" + Replacement = "/* [System.Runtime.CompilerServices.CompilerGeneratedAttribute, System.Runtime.CompilerServices.IsReadOnlyAttribute] */ " + }, @{ ApplyTo = @("System.Management.Automation", "Microsoft.PowerShell.ConsoleHost") Pattern = "[System.Runtime.CompilerServices.NullableAttribute((byte)2)]" From 4500afb681e2cefa70c7815a2bf716bef530a90a Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 21 Jan 2021 17:35:42 -0800 Subject: [PATCH 5/9] Address CodeFactor issues --- .../Subsystem/CommandPrediction/CommandPrediction.cs | 4 ++-- .../Subsystem/CommandPrediction/ICommandPredictor.cs | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs index 385839df847..b0c15cb123d 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs @@ -156,8 +156,8 @@ public static void OnCommandLineAccepted(string client, IReadOnlyList hi /// The identifier of the predictor whose prediction result was accepted. /// The mini-session where the displayed suggestions came from. /// - /// When the value is > 0, it's the number of displayed suggestions from the list returned in , starting from the index 0. - /// When the value is <= 0, it means a single suggestion from the list got displayed, and the index is the absolute value. + /// When the value is greater than 0, it's the number of displayed suggestions from the list returned in , 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. /// public static void OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex) { diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs index ee88dd5c507..37c98c7fb48 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs @@ -58,8 +58,8 @@ public interface ICommandPredictor : ISubsystem /// /// The mini-session where the displayed suggestions came from. /// - /// When the value is > 0, it's the number of displayed suggestions from the list returned in , starting from the index 0. - /// When the value is <= 0, it means a single suggestion from the list got displayed, and the index is the absolute value. + /// When the value is greater than 0, it's the number of displayed suggestions from the list returned in , 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. /// void OnSuggestionDisplayed(uint session, int countOrIndex); @@ -180,18 +180,20 @@ public PredictiveSuggestion(string suggestion, string? toolTip) public struct SuggestionPackage { /// - /// The mini-session that represents a specific invocation to . + /// Gets the mini-session that represents a specific invocation to . /// public uint Session { get; } /// - /// Suggestion entries returned from that mini-session. + /// Gets the suggestion entries returned from that mini-session. /// public List? SuggestionEntries { get; } /// /// Initializes a new instance of the struct. /// + /// The mini-session where suggestions came from. + /// The suggestions to return. public SuggestionPackage(uint session, List suggestionEntries) { Session = session; From e3eb21dbddc8e1e9c46846372e7d768357401ae4 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 22 Jan 2021 10:44:12 -0800 Subject: [PATCH 6/9] Update tests and also add more tests --- test/xUnit/csharp/test_Prediction.cs | 80 ++++++++++++++++++---------- test/xUnit/csharp/test_Subsystem.cs | 10 ++-- 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/test/xUnit/csharp/test_Prediction.cs b/test/xUnit/csharp/test_Prediction.cs index 4d67c850f6d..a034a6b77f1 100644 --- a/test/xUnit/csharp/test_Prediction.cs +++ b/test/xUnit/csharp/test_Prediction.cs @@ -20,9 +20,9 @@ public class MyPredictor : ICommandPredictor public List AcceptedSuggestions { get; } - public static readonly MyPredictor SlowPredictor; + public List DisplayedSuggestions { get; } - public static readonly MyPredictor FastPredictor; + public static readonly MyPredictor SlowPredictor, FastPredictor; static MyPredictor() { @@ -48,6 +48,7 @@ private MyPredictor(Guid id, string name, string description, bool delay) History = new List(); AcceptedSuggestions = new List(); + DisplayedSuggestions = new List(); } public Guid Id => _id; @@ -60,17 +61,25 @@ private MyPredictor(Guid id, string name, string description, bool delay) bool ICommandPredictor.AcceptFeedback => true; - public void StartEarlyProcessing(IReadOnlyList history) + public void StartEarlyProcessing(string clientId, IReadOnlyList history) { - History.AddRange(history); + foreach (string item in history) + { + History.Add($"{clientId}-{item}"); + } + } + + public void OnSuggestionDisplayed(uint session, int countOrIndex) + { + DisplayedSuggestions.Add($"{session}-{countOrIndex}"); } - public void OnSuggestionAccepted(string acceptedSuggestion) + public void OnSuggestionAccepted(uint session, string acceptedSuggestion) { - AcceptedSuggestions.Add(acceptedSuggestion); + AcceptedSuggestions.Add($"{session}-{acceptedSuggestion}"); } - public List GetSuggestion(PredictionContext context, CancellationToken cancellationToken) + public SuggestionPackage GetSuggestion(string clientId, PredictionContext context, CancellationToken cancellationToken) { if (_delay) { @@ -81,24 +90,30 @@ public List GetSuggestion(PredictionContext context, Cance // You can get the user input from the AST. var userInput = context.InputAst.Extent.Text; - return new List { - new PredictiveSuggestion($"{userInput} TEST-1 from {Name}"), - new PredictiveSuggestion($"{userInput} TeSt-2 from {Name}"), + var entries = new List { + new PredictiveSuggestion($"'{userInput}' from '{clientId}' - TEST-1 from {Name}"), + new PredictiveSuggestion($"'{userInput}' from '{clientId}' - TeSt-2 from {Name}"), }; + + return new SuggestionPackage(56, entries); } } public static class CommandPredictionTests { + private const string client = "PredictionTest"; + private const uint session = 56; + [Fact] public static void PredictInput() { + const string input = "Hello world"; MyPredictor slow = MyPredictor.SlowPredictor; MyPredictor fast = MyPredictor.FastPredictor; - Ast ast = Parser.ParseInput("Hello world", out Token[] tokens, out _); + Ast ast = Parser.ParseInput(input, out Token[] tokens, out _); // Returns null when no predictor implementation registered - List results = CommandPrediction.PredictInput(ast, tokens).Result; + List results = CommandPrediction.PredictInput(client, ast, tokens).Result; Assert.Null(results); try @@ -111,32 +126,35 @@ public static void PredictInput() // cannot finish before the specified timeout. // The specified timeout is exaggerated to make the test reliable. // xUnit must spin up a lot tasks, which makes the test unreliable when the time difference between 'delay' and 'timeout' is small. - results = CommandPrediction.PredictInput(ast, tokens, millisecondsTimeout: 1000).Result; + results = CommandPrediction.PredictInput(client, ast, tokens, millisecondsTimeout: 1000).Result; Assert.Single(results); PredictionResult res = results[0]; Assert.Equal(fast.Id, res.Id); + Assert.Equal(session, res.Session); Assert.Equal(2, res.Suggestions.Count); - Assert.Equal($"Hello world TEST-1 from {fast.Name}", res.Suggestions[0].SuggestionText); - Assert.Equal($"Hello world TeSt-2 from {fast.Name}", res.Suggestions[1].SuggestionText); + Assert.Equal($"'{input}' from '{client}' - TEST-1 from {fast.Name}", res.Suggestions[0].SuggestionText); + Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {fast.Name}", res.Suggestions[1].SuggestionText); // Expect the results from both 'slow' and 'fast' predictors // Same here -- the specified timeout is exaggerated to make the test reliable. // xUnit must spin up a lot tasks, which makes the test unreliable when the time difference between 'delay' and 'timeout' is small. - results = CommandPrediction.PredictInput(ast, tokens, millisecondsTimeout: 4000).Result; + results = CommandPrediction.PredictInput(client, ast, tokens, millisecondsTimeout: 4000).Result; Assert.Equal(2, results.Count); PredictionResult res1 = results[0]; Assert.Equal(slow.Id, res1.Id); + Assert.Equal(session, res1.Session); Assert.Equal(2, res1.Suggestions.Count); - Assert.Equal($"Hello world TEST-1 from {slow.Name}", res1.Suggestions[0].SuggestionText); - Assert.Equal($"Hello world TeSt-2 from {slow.Name}", res1.Suggestions[1].SuggestionText); + Assert.Equal($"'{input}' from '{client}' - TEST-1 from {slow.Name}", res1.Suggestions[0].SuggestionText); + Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {slow.Name}", res1.Suggestions[1].SuggestionText); PredictionResult res2 = results[1]; Assert.Equal(fast.Id, res2.Id); + Assert.Equal(session, res2.Session); Assert.Equal(2, res2.Suggestions.Count); - Assert.Equal($"Hello world TEST-1 from {fast.Name}", res2.Suggestions[0].SuggestionText); - Assert.Equal($"Hello world TeSt-2 from {fast.Name}", res2.Suggestions[1].SuggestionText); + Assert.Equal($"'{input}' from '{client}' - TEST-1 from {fast.Name}", res2.Suggestions[0].SuggestionText); + Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {fast.Name}", res2.Suggestions[1].SuggestionText); } finally { @@ -160,8 +178,10 @@ public static void Feedback() var history = new[] { "hello", "world" }; var ids = new HashSet { slow.Id, fast.Id }; - CommandPrediction.OnCommandLineAccepted(history); - CommandPrediction.OnSuggestionAccepted(slow.Id, "Yeah"); + CommandPrediction.OnCommandLineAccepted(client, history); + CommandPrediction.OnSuggestionDisplayed(slow.Id, session, 2); + CommandPrediction.OnSuggestionDisplayed(fast.Id, session, -1); + CommandPrediction.OnSuggestionAccepted(slow.Id, session, "Yeah"); // The calls to 'StartEarlyProcessing' and 'OnSuggestionAccepted' are queued in thread pool, // so we wait a bit to make sure the calls are done. @@ -171,15 +191,21 @@ public static void Feedback() } Assert.Equal(2, slow.History.Count); - Assert.Equal(history[0], slow.History[0]); - Assert.Equal(history[1], slow.History[1]); + Assert.Equal($"{client}-{history[0]}", slow.History[0]); + Assert.Equal($"{client}-{history[1]}", slow.History[1]); Assert.Equal(2, fast.History.Count); - Assert.Equal(history[0], fast.History[0]); - Assert.Equal(history[1], fast.History[1]); + Assert.Equal($"{client}-{history[0]}", fast.History[0]); + Assert.Equal($"{client}-{history[1]}", fast.History[1]); + + Assert.Single(slow.DisplayedSuggestions); + Assert.Equal($"{session}-2", slow.DisplayedSuggestions[0]); + + Assert.Single(fast.DisplayedSuggestions); + Assert.Equal($"{session}--1", fast.DisplayedSuggestions[0]); Assert.Single(slow.AcceptedSuggestions); - Assert.Equal("Yeah", slow.AcceptedSuggestions[0]); + Assert.Equal($"{session}-Yeah", slow.AcceptedSuggestions[0]); Assert.Empty(fast.AcceptedSuggestions); } diff --git a/test/xUnit/csharp/test_Subsystem.cs b/test/xUnit/csharp/test_Subsystem.cs index a45ebc116b5..867a1dd9ff3 100644 --- a/test/xUnit/csharp/test_Subsystem.cs +++ b/test/xUnit/csharp/test_Subsystem.cs @@ -95,10 +95,12 @@ public static void RegisterSubsystem() Assert.Null(impl.FunctionsToDefine); Assert.Equal(SubsystemKind.CommandPredictor, impl.Kind); - var predCxt = PredictionContext.Create("Hello world"); - var results = impl.GetSuggestion(predCxt, CancellationToken.None); - Assert.Equal($"Hello world TEST-1 from {impl.Name}", results[0].SuggestionText); - Assert.Equal($"Hello world TeSt-2 from {impl.Name}", results[1].SuggestionText); + const string client = "SubsystemTest"; + const string input = "Hello world"; + var predCxt = PredictionContext.Create(input); + var results = impl.GetSuggestion(client, predCxt, CancellationToken.None); + Assert.Equal($"'{input}' from '{client}' - TEST-1 from {impl.Name}", results.SuggestionEntries[0].SuggestionText); + Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {impl.Name}", results.SuggestionEntries[1].SuggestionText); // Now validate the all-subsystem-implementation collection. ReadOnlyCollection impls = SubsystemManager.GetSubsystems(); From bea03e397197cfec1a112e76ae071033a65a4408 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 22 Jan 2021 10:52:02 -0800 Subject: [PATCH 7/9] Address codefactor issues --- test/xUnit/csharp/test_Prediction.cs | 57 ++++++++++++++-------------- test/xUnit/csharp/test_Subsystem.cs | 12 +++--- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/test/xUnit/csharp/test_Prediction.cs b/test/xUnit/csharp/test_Prediction.cs index a034a6b77f1..8febad1752f 100644 --- a/test/xUnit/csharp/test_Prediction.cs +++ b/test/xUnit/csharp/test_Prediction.cs @@ -90,7 +90,8 @@ public SuggestionPackage GetSuggestion(string clientId, PredictionContext contex // You can get the user input from the AST. var userInput = context.InputAst.Extent.Text; - var entries = new List { + var entries = new List + { new PredictiveSuggestion($"'{userInput}' from '{clientId}' - TEST-1 from {Name}"), new PredictiveSuggestion($"'{userInput}' from '{clientId}' - TeSt-2 from {Name}"), }; @@ -101,19 +102,19 @@ public SuggestionPackage GetSuggestion(string clientId, PredictionContext contex public static class CommandPredictionTests { - private const string client = "PredictionTest"; - private const uint session = 56; + private const string Client = "PredictionTest"; + private const uint Session = 56; [Fact] public static void PredictInput() { - const string input = "Hello world"; + const string Input = "Hello world"; MyPredictor slow = MyPredictor.SlowPredictor; MyPredictor fast = MyPredictor.FastPredictor; - Ast ast = Parser.ParseInput(input, out Token[] tokens, out _); + Ast ast = Parser.ParseInput(Input, out Token[] tokens, out _); // Returns null when no predictor implementation registered - List results = CommandPrediction.PredictInput(client, ast, tokens).Result; + List results = CommandPrediction.PredictInput(Client, ast, tokens).Result; Assert.Null(results); try @@ -126,35 +127,35 @@ public static void PredictInput() // cannot finish before the specified timeout. // The specified timeout is exaggerated to make the test reliable. // xUnit must spin up a lot tasks, which makes the test unreliable when the time difference between 'delay' and 'timeout' is small. - results = CommandPrediction.PredictInput(client, ast, tokens, millisecondsTimeout: 1000).Result; + results = CommandPrediction.PredictInput(Client, ast, tokens, millisecondsTimeout: 1000).Result; Assert.Single(results); PredictionResult res = results[0]; Assert.Equal(fast.Id, res.Id); - Assert.Equal(session, res.Session); + Assert.Equal(Session, res.Session); Assert.Equal(2, res.Suggestions.Count); - Assert.Equal($"'{input}' from '{client}' - TEST-1 from {fast.Name}", res.Suggestions[0].SuggestionText); - Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {fast.Name}", res.Suggestions[1].SuggestionText); + Assert.Equal($"'{Input}' from '{Client}' - TEST-1 from {fast.Name}", res.Suggestions[0].SuggestionText); + Assert.Equal($"'{Input}' from '{Client}' - TeSt-2 from {fast.Name}", res.Suggestions[1].SuggestionText); // Expect the results from both 'slow' and 'fast' predictors // Same here -- the specified timeout is exaggerated to make the test reliable. // xUnit must spin up a lot tasks, which makes the test unreliable when the time difference between 'delay' and 'timeout' is small. - results = CommandPrediction.PredictInput(client, ast, tokens, millisecondsTimeout: 4000).Result; + results = CommandPrediction.PredictInput(Client, ast, tokens, millisecondsTimeout: 4000).Result; Assert.Equal(2, results.Count); PredictionResult res1 = results[0]; Assert.Equal(slow.Id, res1.Id); - Assert.Equal(session, res1.Session); + Assert.Equal(Session, res1.Session); Assert.Equal(2, res1.Suggestions.Count); - Assert.Equal($"'{input}' from '{client}' - TEST-1 from {slow.Name}", res1.Suggestions[0].SuggestionText); - Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {slow.Name}", res1.Suggestions[1].SuggestionText); + Assert.Equal($"'{Input}' from '{Client}' - TEST-1 from {slow.Name}", res1.Suggestions[0].SuggestionText); + Assert.Equal($"'{Input}' from '{Client}' - TeSt-2 from {slow.Name}", res1.Suggestions[1].SuggestionText); PredictionResult res2 = results[1]; Assert.Equal(fast.Id, res2.Id); - Assert.Equal(session, res2.Session); + Assert.Equal(Session, res2.Session); Assert.Equal(2, res2.Suggestions.Count); - Assert.Equal($"'{input}' from '{client}' - TEST-1 from {fast.Name}", res2.Suggestions[0].SuggestionText); - Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {fast.Name}", res2.Suggestions[1].SuggestionText); + Assert.Equal($"'{Input}' from '{Client}' - TEST-1 from {fast.Name}", res2.Suggestions[0].SuggestionText); + Assert.Equal($"'{Input}' from '{Client}' - TeSt-2 from {fast.Name}", res2.Suggestions[1].SuggestionText); } finally { @@ -178,10 +179,10 @@ public static void Feedback() var history = new[] { "hello", "world" }; var ids = new HashSet { slow.Id, fast.Id }; - CommandPrediction.OnCommandLineAccepted(client, history); - CommandPrediction.OnSuggestionDisplayed(slow.Id, session, 2); - CommandPrediction.OnSuggestionDisplayed(fast.Id, session, -1); - CommandPrediction.OnSuggestionAccepted(slow.Id, session, "Yeah"); + CommandPrediction.OnCommandLineAccepted(Client, history); + CommandPrediction.OnSuggestionDisplayed(slow.Id, Session, 2); + CommandPrediction.OnSuggestionDisplayed(fast.Id, Session, -1); + CommandPrediction.OnSuggestionAccepted(slow.Id, Session, "Yeah"); // The calls to 'StartEarlyProcessing' and 'OnSuggestionAccepted' are queued in thread pool, // so we wait a bit to make sure the calls are done. @@ -191,21 +192,21 @@ public static void Feedback() } Assert.Equal(2, slow.History.Count); - Assert.Equal($"{client}-{history[0]}", slow.History[0]); - Assert.Equal($"{client}-{history[1]}", slow.History[1]); + Assert.Equal($"{Client}-{history[0]}", slow.History[0]); + Assert.Equal($"{Client}-{history[1]}", slow.History[1]); Assert.Equal(2, fast.History.Count); - Assert.Equal($"{client}-{history[0]}", fast.History[0]); - Assert.Equal($"{client}-{history[1]}", fast.History[1]); + Assert.Equal($"{Client}-{history[0]}", fast.History[0]); + Assert.Equal($"{Client}-{history[1]}", fast.History[1]); Assert.Single(slow.DisplayedSuggestions); - Assert.Equal($"{session}-2", slow.DisplayedSuggestions[0]); + Assert.Equal($"{Session}-2", slow.DisplayedSuggestions[0]); Assert.Single(fast.DisplayedSuggestions); - Assert.Equal($"{session}--1", fast.DisplayedSuggestions[0]); + Assert.Equal($"{Session}--1", fast.DisplayedSuggestions[0]); Assert.Single(slow.AcceptedSuggestions); - Assert.Equal($"{session}-Yeah", slow.AcceptedSuggestions[0]); + Assert.Equal($"{Session}-Yeah", slow.AcceptedSuggestions[0]); Assert.Empty(fast.AcceptedSuggestions); } diff --git a/test/xUnit/csharp/test_Subsystem.cs b/test/xUnit/csharp/test_Subsystem.cs index 867a1dd9ff3..f2ca308221f 100644 --- a/test/xUnit/csharp/test_Subsystem.cs +++ b/test/xUnit/csharp/test_Subsystem.cs @@ -95,12 +95,12 @@ public static void RegisterSubsystem() Assert.Null(impl.FunctionsToDefine); Assert.Equal(SubsystemKind.CommandPredictor, impl.Kind); - const string client = "SubsystemTest"; - const string input = "Hello world"; - var predCxt = PredictionContext.Create(input); - var results = impl.GetSuggestion(client, predCxt, CancellationToken.None); - Assert.Equal($"'{input}' from '{client}' - TEST-1 from {impl.Name}", results.SuggestionEntries[0].SuggestionText); - Assert.Equal($"'{input}' from '{client}' - TeSt-2 from {impl.Name}", results.SuggestionEntries[1].SuggestionText); + const string Client = "SubsystemTest"; + const string Input = "Hello world"; + var predCxt = PredictionContext.Create(Input); + var results = impl.GetSuggestion(Client, predCxt, CancellationToken.None); + Assert.Equal($"'{Input}' from '{Client}' - TEST-1 from {impl.Name}", results.SuggestionEntries[0].SuggestionText); + Assert.Equal($"'{Input}' from '{Client}' - TeSt-2 from {impl.Name}", results.SuggestionEntries[1].SuggestionText); // Now validate the all-subsystem-implementation collection. ReadOnlyCollection impls = SubsystemManager.GetSubsystems(); From 6bf5817920317a6dffe198b3039b17e78d9b69b5 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 28 Jan 2021 13:05:43 -0800 Subject: [PATCH 8/9] Pass 'ClientId' to 'OnSuggestionDisplay' and 'OnSuggestionAccepted' and also make 'Session' use 'uint?' type --- .../CommandPrediction/CommandPrediction.cs | 15 ++++++----- .../CommandPrediction/ICommandPredictor.cs | 25 ++++++++++++++++--- .../engine/Utils.cs | 8 ++++++ test/xUnit/csharp/test_Prediction.cs | 20 +++++++-------- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs index b0c15cb123d..edc8fb272a2 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/CommandPrediction.cs @@ -29,15 +29,16 @@ public sealed class PredictionResult /// /// Gets the mini-session id that represents a specific invocation to the API of the predictor. + /// When it's not specified, it's considered by a client that the predictor doesn't expect feedback. /// - public uint Session { get; } + public uint? Session { get; } /// /// Gets the suggestions. /// public IReadOnlyList Suggestions { get; } - internal PredictionResult(Guid id, string name, uint session, List suggestions) + internal PredictionResult(Guid id, string name, uint? session, List suggestions) { Id = id; Name = name; @@ -153,13 +154,14 @@ public static void OnCommandLineAccepted(string client, IReadOnlyList hi /// /// Send feedback to a predictor when one or more suggestions from it were displayed to the user. /// + /// Represents the client that initiates the call. /// The identifier of the predictor whose prediction result was accepted. /// The mini-session where the displayed suggestions came from. /// /// When the value is greater than 0, it's the number of displayed suggestions from the list returned in , 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. /// - public static void OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex) + public static void OnSuggestionDisplayed(string client, Guid predictorId, uint session, int countOrIndex) { var predictors = SubsystemManager.GetSubsystems(); if (predictors.Count == 0) @@ -172,7 +174,7 @@ public static void OnSuggestionDisplayed(Guid predictorId, uint session, int cou if (predictor.AcceptFeedback && predictor.Id == predictorId) { ThreadPool.QueueUserWorkItem( - state => state.OnSuggestionDisplayed(session, countOrIndex), + state => state.OnSuggestionDisplayed(client, session, countOrIndex), predictor, preferLocal: false); } @@ -182,10 +184,11 @@ public static void OnSuggestionDisplayed(Guid predictorId, uint session, int cou /// /// Send feedback to a predictor when a suggestion from it was accepted. /// + /// Represents the client that initiates the call. /// The identifier of the predictor whose prediction result was accepted. /// The mini-session where the accepted suggestion came from. /// The accepted suggestion text. - public static void OnSuggestionAccepted(Guid predictorId, uint session, string suggestionText) + public static void OnSuggestionAccepted(string client, Guid predictorId, uint session, string suggestionText) { Requires.NotNullOrEmpty(suggestionText, nameof(suggestionText)); @@ -200,7 +203,7 @@ public static void OnSuggestionAccepted(Guid predictorId, uint session, string s if (predictor.AcceptFeedback && predictor.Id == predictorId) { ThreadPool.QueueUserWorkItem( - state => state.OnSuggestionAccepted(session, suggestionText), + state => state.OnSuggestionAccepted(client, session, suggestionText), predictor, preferLocal: false); } diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs index 37c98c7fb48..3b8b6d5a8d7 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs @@ -56,19 +56,21 @@ public interface ICommandPredictor : ISubsystem /// /// One or more suggestions provided by the predictor were displayed to the user. /// + /// Represents the client that initiates the call. /// The mini-session where the displayed suggestions came from. /// /// When the value is greater than 0, it's the number of displayed suggestions from the list returned in , 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. /// - void OnSuggestionDisplayed(uint session, int countOrIndex); + void OnSuggestionDisplayed(string clientId, uint session, int countOrIndex); /// /// The suggestion provided by the predictor was accepted. /// + /// Represents the client that initiates the call. /// Represents the mini-session where the accepted suggestion came from. /// The accepted suggestion text. - void OnSuggestionAccepted(uint session, string acceptedSuggestion); + void OnSuggestionAccepted(string clientId, uint session, string acceptedSuggestion); } /// @@ -181,8 +183,9 @@ public struct SuggestionPackage { /// /// Gets the mini-session that represents a specific invocation to . + /// When it's not specified, it's considered by a client that the predictor doesn't expect feedback. /// - public uint Session { get; } + public uint? Session { get; } /// /// Gets the suggestion entries returned from that mini-session. @@ -190,12 +193,26 @@ public struct SuggestionPackage public List? SuggestionEntries { get; } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct without providing a session id. + /// + /// The suggestions to return. + public SuggestionPackage(List suggestionEntries) + { + Requires.NotNullOrEmpty(suggestionEntries, nameof(suggestionEntries)); + + Session = null; + SuggestionEntries = suggestionEntries; + } + + /// + /// Initializes a new instance of the struct with the mini-session id and the suggestions. /// /// The mini-session where suggestions came from. /// The suggestions to return. public SuggestionPackage(uint session, List suggestionEntries) { + Requires.NotNullOrEmpty(suggestionEntries, nameof(suggestionEntries)); + Session = session; SuggestionEntries = suggestionEntries; } diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 6a4a863d23c..fa1ef13572e 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -2312,6 +2312,14 @@ internal static void NotNullOrEmpty(string value, string paramName) } } + internal static void NotNullOrEmpty(ICollection value, string paramName) + { + if (value == null || value.Count == 0) + { + throw new ArgumentNullException(paramName); + } + } + internal static void Condition([DoesNotReturnIf(false)] bool precondition, string paramName) { if (!precondition) diff --git a/test/xUnit/csharp/test_Prediction.cs b/test/xUnit/csharp/test_Prediction.cs index 8febad1752f..8f018ebb9e1 100644 --- a/test/xUnit/csharp/test_Prediction.cs +++ b/test/xUnit/csharp/test_Prediction.cs @@ -69,14 +69,14 @@ public void StartEarlyProcessing(string clientId, IReadOnlyList history) } } - public void OnSuggestionDisplayed(uint session, int countOrIndex) + public void OnSuggestionDisplayed(string clientId, uint session, int countOrIndex) { - DisplayedSuggestions.Add($"{session}-{countOrIndex}"); + DisplayedSuggestions.Add($"{clientId}-{session}-{countOrIndex}"); } - public void OnSuggestionAccepted(uint session, string acceptedSuggestion) + public void OnSuggestionAccepted(string clientId, uint session, string acceptedSuggestion) { - AcceptedSuggestions.Add($"{session}-{acceptedSuggestion}"); + AcceptedSuggestions.Add($"{clientId}-{session}-{acceptedSuggestion}"); } public SuggestionPackage GetSuggestion(string clientId, PredictionContext context, CancellationToken cancellationToken) @@ -180,9 +180,9 @@ public static void Feedback() var ids = new HashSet { slow.Id, fast.Id }; CommandPrediction.OnCommandLineAccepted(Client, history); - CommandPrediction.OnSuggestionDisplayed(slow.Id, Session, 2); - CommandPrediction.OnSuggestionDisplayed(fast.Id, Session, -1); - CommandPrediction.OnSuggestionAccepted(slow.Id, Session, "Yeah"); + CommandPrediction.OnSuggestionDisplayed(Client, slow.Id, Session, 2); + CommandPrediction.OnSuggestionDisplayed(Client, fast.Id, Session, -1); + CommandPrediction.OnSuggestionAccepted(Client, slow.Id, Session, "Yeah"); // The calls to 'StartEarlyProcessing' and 'OnSuggestionAccepted' are queued in thread pool, // so we wait a bit to make sure the calls are done. @@ -200,13 +200,13 @@ public static void Feedback() Assert.Equal($"{Client}-{history[1]}", fast.History[1]); Assert.Single(slow.DisplayedSuggestions); - Assert.Equal($"{Session}-2", slow.DisplayedSuggestions[0]); + Assert.Equal($"{Client}-{Session}-2", slow.DisplayedSuggestions[0]); Assert.Single(fast.DisplayedSuggestions); - Assert.Equal($"{Session}--1", fast.DisplayedSuggestions[0]); + Assert.Equal($"{Client}-{Session}--1", fast.DisplayedSuggestions[0]); Assert.Single(slow.AcceptedSuggestions); - Assert.Equal($"{Session}-Yeah", slow.AcceptedSuggestions[0]); + Assert.Equal($"{Client}-{Session}-Yeah", slow.AcceptedSuggestions[0]); Assert.Empty(fast.AcceptedSuggestions); } From 0b7e42d09bf49ecf72bfa8eb6a08331c54a2ab75 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Sat, 30 Jan 2021 21:18:17 -0800 Subject: [PATCH 9/9] Add the API xml document files to the generated NuGet packages --- .../CommandPrediction/ICommandPredictor.cs | 1 + tools/packaging/packaging.psm1 | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs index 3b8b6d5a8d7..d55d37a90df 100644 --- a/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs +++ b/src/System.Management.Automation/engine/Subsystem/CommandPrediction/ICommandPredictor.cs @@ -194,6 +194,7 @@ public struct SuggestionPackage /// /// Initializes a new instance of the struct without providing a session id. + /// Note that, when a session id is not specified, it's considered by a client that the predictor doesn't expect feedback. /// /// The suggestions to return. public SuggestionPackage(List suggestionEntries) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 3d996f338aa..548f724caec 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -2036,24 +2036,27 @@ function CopyReferenceAssemblies switch ($assemblyName) { { $_ -in $supportedRefList } { $refDll = Join-Path -Path $refBinPath -ChildPath "$assemblyName.dll" - Copy-Item $refDll -Destination $refNugetPath -Force - Write-Log "Copied file $refDll to $refNugetPath" + $refDoc = Join-Path -Path $refBinPath -ChildPath "$assemblyName.xml" + Copy-Item $refDll, $refDoc -Destination $refNugetPath -Force + Write-Log "Copied file '$refDll' and '$refDoc' to '$refNugetPath'" } "Microsoft.PowerShell.SDK" { foreach ($asmFileName in $assemblyFileList) { $refFile = Join-Path -Path $refBinPath -ChildPath $asmFileName if (Test-Path -Path $refFile) { - Copy-Item $refFile -Destination $refNugetPath -Force - Write-Log "Copied file $refFile to $refNugetPath" + $refDoc = Join-Path -Path $refBinPath -ChildPath ([System.IO.Path]::ChangeExtension($asmFileName, "xml")) + Copy-Item $refFile, $refDoc -Destination $refNugetPath -Force + Write-Log "Copied file '$refFile' and '$refDoc' to '$refNugetPath'" } } } default { $ref_SMA = Join-Path -Path $refBinPath -ChildPath System.Management.Automation.dll - Copy-Item $ref_SMA -Destination $refNugetPath -Force - Write-Log "Copied file $ref_SMA to $refNugetPath" + $ref_doc = Join-Path -Path $refBinPath -ChildPath System.Management.Automation.xml + Copy-Item $ref_SMA, $ref_doc -Destination $refNugetPath -Force + Write-Log "Copied file '$ref_SMA' and '$ref_doc' to '$refNugetPath'" } } } @@ -2235,8 +2238,13 @@ function New-ReferenceAssembly throw "$assemblyName.dll was not found at: $Linux64BinPath" } + $dllXmlDoc = Join-Path $Linux64BinPath "$assemblyName.xml" + if (-not (Test-Path $dllXmlDoc)) { + throw "$assemblyName.xml was not found at: $Linux64BinPath" + } + $genAPIArgs = "$linuxDllPath","-libPath:$Linux64BinPath,$Linux64BinPath\ref" - Write-Log "GenAPI cmd: $genAPIExe $genAPIArgsString" + Write-Log "GenAPI cmd: $genAPIExe $genAPIArgs" Start-NativeExecution { & $genAPIExe $genAPIArgs } | Out-File $generatedSource -Force Write-Log "Reference assembly file generated at: $generatedSource" @@ -2268,6 +2276,9 @@ function New-ReferenceAssembly Copy-Item $refBinPath $RefAssemblyDestinationPath -Force Write-Log "Reference assembly '$assemblyName.dll' built and copied to $RefAssemblyDestinationPath" + Copy-Item $dllXmlDoc $RefAssemblyDestinationPath -Force + Write-Log "Xml document '$assemblyName.xml' copied to $RefAssemblyDestinationPath" + if ($assemblyName -eq "System.Management.Automation") { $SMAReferenceAssembly = $refBinPath }