1
+ using System . Diagnostics . CodeAnalysis ;
1
2
using System . Management . Automation . Language ;
2
3
using System . Management . Automation . Runspaces ;
3
4
using System . Management . Automation . Subsystem . Prediction ;
@@ -10,6 +11,8 @@ public partial class CompletionPredictor : ICommandPredictor, IDisposable
10
11
{
11
12
private readonly Guid _guid ;
12
13
private readonly Runspace _runspace ;
14
+ private readonly GitHandler _gitHandler ;
15
+ private string ? _cwd ;
13
16
private int _lock = 1 ;
14
17
15
18
private static HashSet < string > s_cmdList = new ( StringComparer . OrdinalIgnoreCase )
@@ -21,11 +24,13 @@ public partial class CompletionPredictor : ICommandPredictor, IDisposable
21
24
"where" ,
22
25
"Where-Object" ,
23
26
"cd" ,
27
+ "git" ,
24
28
} ;
25
29
26
30
internal CompletionPredictor ( string guid )
27
31
{
28
32
_guid = new Guid ( guid ) ;
33
+ _gitHandler = new GitHandler ( ) ;
29
34
_runspace = RunspaceFactory . CreateRunspace ( InitialSessionState . CreateDefault ( ) ) ;
30
35
_runspace . Name = nameof ( CompletionPredictor ) ;
31
36
_runspace . Open ( ) ;
@@ -47,25 +52,55 @@ public SuggestionPackage GetSuggestion(PredictionClient client, PredictionContex
47
52
{
48
53
// When it ends at a white space, it would likely trigger argument completion which in most cases would be file-operation
49
54
// intensive. That's not only slow but also undesirable in most cases, so we skip it.
50
- // But, there are exceptions for 'ForEach-Object' and 'Where-Object', where completion on member names is quite useful.
51
- Ast lastAst = relatedAsts [ ^ 1 ] ;
52
- var cmdName = ( lastAst . Parent as CommandAst ) ? . CommandElements [ 0 ] as StringConstantExpressionAst ;
53
- if ( cmdName is null || ! s_cmdList . Contains ( cmdName . Value ) || ! object . ReferenceEquals ( lastAst , cmdName ) )
55
+ // But, there are exceptions for some commands, where completion on member names is quite useful.
56
+ if ( ! IsCommandAstWithLiteralName ( context , out var cmdAst , out var nameAst )
57
+ || ! s_cmdList . TryGetValue ( nameAst . Value , out string ? cmd ) )
54
58
{
55
- // So we stop processing unless the cursor is right after 'ForEach-Object' or 'Where-Object' .
59
+ // Stop processing if the cursor is not at the end of an allowed command .
56
60
return default ;
57
61
}
58
- }
59
62
60
- if ( tokenAtCursor is not null && tokenAtCursor . TokenFlags . HasFlag ( TokenFlags . CommandName ) )
63
+ if ( cmd is "git" )
64
+ {
65
+ // Process 'git' command.
66
+ return _gitHandler . GetGitResult ( cmdAst , _cwd , context , cancellationToken ) ;
67
+ }
68
+
69
+ if ( cmdAst . CommandElements . Count != 1 )
70
+ {
71
+ // For commands other than 'git', we only do argument completion if the cursor is right after the command name.
72
+ return default ;
73
+ }
74
+ }
75
+ else
61
76
{
62
- // When it's a command, it would likely take too much time because the command discovery is usually expensive, so we skip it.
63
- return default ;
77
+ if ( tokenAtCursor . TokenFlags . HasFlag ( TokenFlags . CommandName ) )
78
+ {
79
+ // When it's a command, it would likely take too much time because the command discovery is usually expensive, so we skip it.
80
+ return default ;
81
+ }
82
+
83
+ if ( IsCommandAstWithLiteralName ( context , out var cmdAst , out var nameAst )
84
+ && string . Equals ( nameAst . Value , "git" , StringComparison . OrdinalIgnoreCase ) )
85
+ {
86
+ return _gitHandler . GetGitResult ( cmdAst , _cwd , context , cancellationToken ) ;
87
+ }
64
88
}
65
89
66
90
return GetFromTabCompletion ( context , cancellationToken ) ;
67
91
}
68
92
93
+ private bool IsCommandAstWithLiteralName (
94
+ PredictionContext context ,
95
+ [ NotNullWhen ( true ) ] out CommandAst ? cmdAst ,
96
+ [ NotNullWhen ( true ) ] out StringConstantExpressionAst ? nameAst )
97
+ {
98
+ Ast lastAst = context . RelatedAsts [ ^ 1 ] ;
99
+ cmdAst = lastAst . Parent as CommandAst ;
100
+ nameAst = cmdAst ? . CommandElements [ 0 ] as StringConstantExpressionAst ;
101
+ return nameAst is not null ;
102
+ }
103
+
69
104
private SuggestionPackage GetFromTabCompletion ( PredictionContext context , CancellationToken cancellationToken )
70
105
{
71
106
// Call into PowerShell tab completion to get completion results.
@@ -155,11 +190,18 @@ public void Dispose()
155
190
156
191
#region "Unused interface members because this predictor doesn't process feedback"
157
192
158
- public bool CanAcceptFeedback ( PredictionClient client , PredictorFeedbackKind feedback ) => false ;
193
+ public bool CanAcceptFeedback ( PredictionClient client , PredictorFeedbackKind feedback )
194
+ {
195
+ return feedback == PredictorFeedbackKind . CommandLineAccepted ? true : false ;
196
+ }
197
+
159
198
public void OnSuggestionDisplayed ( PredictionClient client , uint session , int countOrIndex ) { }
160
199
public void OnSuggestionAccepted ( PredictionClient client , uint session , string acceptedSuggestion ) { }
161
- public void OnCommandLineAccepted ( PredictionClient client , IReadOnlyList < string > history ) { }
162
200
public void OnCommandLineExecuted ( PredictionClient client , string commandLine , bool success ) { }
201
+ public void OnCommandLineAccepted ( PredictionClient client , IReadOnlyList < string > history )
202
+ {
203
+ _gitHandler . SignalCheckForRepoUpdate ( ) ;
204
+ }
163
205
164
206
#endregion;
165
207
}
0 commit comments