@@ -29,7 +29,7 @@ namespace Microsoft.Azure.PowerShell.Tools.AzPredictor
29
29
/// <summary>
30
30
/// The implementation of a <see cref="ICommandPredictor"/> to provide suggestion in PSReadLine.
31
31
/// </summary>
32
- public sealed class AzPredictor : ICommandPredictor
32
+ internal sealed class AzPredictor : ICommandPredictor
33
33
{
34
34
/// <inhericdoc />
35
35
public string Name => "Az Predictor" ;
@@ -44,7 +44,7 @@ public sealed class AzPredictor : ICommandPredictor
44
44
public bool SupportEarlyProcessing => true ;
45
45
46
46
/// <inhericdoc />
47
- public bool AcceptFeedback => false ;
47
+ public bool AcceptFeedback => true ;
48
48
49
49
internal static readonly Guid Identifier = new Guid ( "599d1760-4ee1-4ed2-806e-f2a1b1a0ba4d" ) ;
50
50
@@ -56,52 +56,85 @@ public sealed class AzPredictor : ICommandPredictor
56
56
57
57
private readonly IAzPredictorService _service ;
58
58
private readonly ITelemetryClient _telemetryClient ;
59
+ private readonly Settings _settings ;
60
+
61
+ private Queue < string > _lastTwoMaskedCommands = new Queue < string > ( AzPredictorConstants . CommandHistoryCountToProcess ) ;
59
62
60
63
/// <summary>
61
64
/// Constructs a new instance of <see cref="AzPredictor"/>
62
65
/// </summary>
63
66
/// <param name="service">The service that provides the suggestion</param>
64
67
/// <param name="telemetryClient">The client to collect telemetry</param>
65
- public AzPredictor ( IAzPredictorService service , ITelemetryClient telemetryClient )
68
+ /// <param name="settings">The settings of the service</param>
69
+ public AzPredictor ( IAzPredictorService service , ITelemetryClient telemetryClient , Settings settings )
66
70
{
67
71
this . _service = service ;
68
72
this . _telemetryClient = telemetryClient ;
73
+ this . _settings = settings ;
69
74
}
70
75
71
76
/// <inhericdoc />
72
77
public void StartEarlyProcessing ( IReadOnlyList < string > history )
73
78
{
74
79
if ( history . Count > 0 )
75
80
{
76
- var historyLines = history . TakeLast ( AzPredictorConstants . CommandHistoryCountToProcess ) ;
81
+ if ( _lastTwoMaskedCommands . Any ( ) )
82
+ {
83
+ _lastTwoMaskedCommands . Dequeue ( ) ;
84
+ }
85
+ else
86
+ {
87
+ // This is the first time we populate our record. Push the second to last command in history to the
88
+ // queue. If there is only one command in history, push the command placeholder.
89
+
90
+ if ( history . Count ( ) > 1 )
91
+ {
92
+ string secondToLastLine = history . TakeLast ( AzPredictorConstants . CommandHistoryCountToProcess ) . First ( ) ;
93
+ var secondToLastCommand = GetAstAndMaskedCommandLine ( secondToLastLine ) ;
94
+ _lastTwoMaskedCommands . Enqueue ( secondToLastCommand . Item2 ) ;
95
+ _service . RecordHistory ( secondToLastCommand . Item1 ) ;
96
+ }
97
+ else
98
+ {
99
+ _lastTwoMaskedCommands . Enqueue ( AzPredictorConstants . CommandPlaceholder ) ;
100
+ // We only extract parameter values from the command line in _service.RecordHistory.
101
+ // So we don't need to do that for a placeholder.
102
+ }
103
+ }
104
+
105
+ string lastLine = history . Last ( ) ;
106
+ var lastCommand = GetAstAndMaskedCommandLine ( lastLine ) ;
77
107
78
- while ( historyLines . Count ( ) < AzPredictorConstants . CommandHistoryCountToProcess )
108
+ _lastTwoMaskedCommands . Enqueue ( lastCommand . Item2 ) ;
109
+
110
+ if ( ( lastCommand . Item2 != null ) && ! string . Equals ( AzPredictorConstants . CommandPlaceholder , lastCommand . Item2 , StringComparison . Ordinal ) )
79
111
{
80
- historyLines = historyLines . Prepend ( AzPredictorConstants . CommandPlaceholder ) ;
112
+ _service . RecordHistory ( lastCommand . Item1 ) ;
81
113
}
82
114
83
- var commandAsts = historyLines . Select ( ( h ) =>
84
- {
85
- var ast = Parser . ParseInput ( h , out Token [ ] tokens , out _ ) ;
86
- var allAsts = ast ? . FindAll ( ( ast ) => ast is CommandAst , true ) ;
87
- return allAsts ? . LastOrDefault ( ) as CommandAst ;
88
- } ) . ToArray ( ) ;
115
+ _telemetryClient . OnHistory ( lastCommand . Item2 ) ;
116
+ _service . RequestPredictions ( _lastTwoMaskedCommands ) ;
117
+ }
89
118
90
- var maskedHistoryLines = commandAsts . Select ( ( c ) =>
91
- {
92
- var commandName = c ? . CommandElements ? . FirstOrDefault ( ) . ToString ( ) ;
119
+ ValueTuple < CommandAst , string > GetAstAndMaskedCommandLine ( string commandLine )
120
+ {
121
+ var asts = Parser . ParseInput ( commandLine , out _ , out _ ) ;
122
+ var allNestedAsts = asts ? . FindAll ( ( ast ) => ast is CommandAst , true ) ;
123
+ var commandAst = allNestedAsts ? . LastOrDefault ( ) as CommandAst ;
124
+ string maskedCommandLine = null ;
93
125
94
- if ( ! _service . IsSupportedCommand ( commandName ) )
95
- {
96
- return AzPredictorConstants . CommandPlaceholder ;
97
- }
126
+ var commandName = commandAst ? . CommandElements ? . FirstOrDefault ( ) . ToString ( ) ;
98
127
99
- return AzPredictor . MaskCommandLine ( c ) ;
100
- } ) ;
128
+ if ( _service . IsSupportedCommand ( commandName ) )
129
+ {
130
+ maskedCommandLine = AzPredictor . MaskCommandLine ( commandAst ) ;
131
+ }
132
+ else
133
+ {
134
+ maskedCommandLine = AzPredictorConstants . CommandPlaceholder ;
135
+ }
101
136
102
- _telemetryClient . OnHistory ( maskedHistoryLines . Last ( ) ) ;
103
- _service . RecordHistory ( commandAsts ) ;
104
- _service . RequestPredictions ( maskedHistoryLines ) ;
137
+ return ValueTuple . Create ( commandAst , maskedCommandLine ) ;
105
138
}
106
139
}
107
140
@@ -121,19 +154,19 @@ public List<PredictiveSuggestion> GetSuggestion(PredictionContext context, Cance
121
154
// is prefixed with `userInput`, it should be ordered before result that is not prefixed
122
155
// with `userInput`.
123
156
124
- Tuple < string , PredictionSource > result = Tuple . Create < string , PredictionSource > ( null , PredictionSource . None ) ;
157
+ IEnumerable < ValueTuple < string , PredictionSource > > suggestions = Enumerable . Empty < ValueTuple < string , PredictionSource > > ( ) ;
125
158
126
159
try
127
160
{
128
- result = _service . GetSuggestion ( context . InputAst , cancellationToken ) ;
161
+ suggestions = _service . GetSuggestion ( context . InputAst , _settings . SuggestionCount . Value , cancellationToken ) ;
129
162
130
- if ( result ? . Item1 != null )
131
- {
132
- cancellationToken . ThrowIfCancellationRequested ( ) ;
133
- var userInput = context . InputAst . Extent . Text ;
134
- var fullSuggestion = MergeStrings ( userInput , result . Item1 ) ;
135
- return new List < PredictiveSuggestion > ( ) { new PredictiveSuggestion ( fullSuggestion ) } ;
136
- }
163
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
164
+ var userInput = context . InputAst . Extent . Text ;
165
+ return suggestions . Select ( ( r , index ) =>
166
+ {
167
+ return new PredictiveSuggestion ( MergeStrings ( userInput , r . Item1 ) ) ;
168
+ } )
169
+ . ToList ( ) ;
137
170
}
138
171
catch ( Exception e ) when ( ! ( e is OperationCanceledException ) )
139
172
{
@@ -142,11 +175,10 @@ public List<PredictiveSuggestion> GetSuggestion(PredictionContext context, Cance
142
175
finally
143
176
{
144
177
var maskedCommandLine = MaskCommandLine ( context . InputAst . FindAll ( ( ast ) => ast is CommandAst , true ) . LastOrDefault ( ) as CommandAst ) ;
145
- _telemetryClient . OnGetSuggestion ( maskedCommandLine , new Tuple < string , PredictionSource > [ ] { result } ,
146
- cancellationToken . IsCancellationRequested ) ;
178
+ _telemetryClient . OnGetSuggestion ( maskedCommandLine , suggestions , cancellationToken . IsCancellationRequested ) ;
147
179
}
148
180
149
- return null ;
181
+ return new List < PredictiveSuggestion > ( ) ;
150
182
}
151
183
152
184
// Merge strings a and b such that the prefix of b is deleted if it is the suffix of a
@@ -232,7 +264,7 @@ public void OnImport()
232
264
var settings = Settings . GetSettings ( ) ;
233
265
var telemetryClient = new AzPredictorTelemetryClient ( ) ;
234
266
var azPredictorService = new AzPredictorService ( settings . ServiceUri , telemetryClient ) ;
235
- var predictor = new AzPredictor ( azPredictorService , telemetryClient ) ;
267
+ var predictor = new AzPredictor ( azPredictorService , telemetryClient , settings ) ;
236
268
SubsystemManager . RegisterSubsystem < ICommandPredictor , AzPredictor > ( predictor ) ;
237
269
}
238
270
}
0 commit comments