@@ -27,6 +27,13 @@ public class Channel : IDisposable
27
27
private Thread _serverThread ;
28
28
private CodePostData _pendingPostCodeData ;
29
29
30
+ /// <summary>
31
+ /// Indicates if a code-post message is being processed:
32
+ /// - value is 1: yes;
33
+ /// - value is 0: no.
34
+ /// </summary>
35
+ private int _processingCodePost ;
36
+
30
37
private Channel ( Runspace runspace , Type psConsoleReadLineType )
31
38
{
32
39
_runspace = runspace ;
@@ -183,64 +190,79 @@ private void OnPostCode(PostCodeMessage postCodeMessage)
183
190
{
184
191
// Ignore 'code post' request when a posting operation is on-going.
185
192
// This most likely would happen when user run 'code post' mutliple times to post the same code, which is safe to ignore.
186
- if ( _pendingPostCodeData is not null || postCodeMessage . CodeBlocks . Count is 0 )
193
+ if ( Interlocked . CompareExchange ( ref _processingCodePost , 1 , 0 ) is 1 )
187
194
{
188
195
return ;
189
196
}
190
197
191
- string codeToInsert ;
192
- List < string > codeBlocks = postCodeMessage . CodeBlocks ;
193
- List < PredictionCandidate > predictionCandidates = null ;
194
-
195
- if ( codeBlocks . Count is 1 )
196
- {
197
- codeToInsert = codeBlocks [ 0 ] ;
198
- }
199
- else if ( Predictor . TryProcessForPrediction ( codeBlocks , out predictionCandidates ) )
200
- {
201
- codeToInsert = predictionCandidates [ 0 ] . Code ;
202
- }
203
- else
198
+ try
204
199
{
205
- // Use LF as line ending to be consistent with the response from LLM.
206
- StringBuilder sb = new ( capacity : 50 ) ;
207
- for ( int i = 0 ; i < codeBlocks . Count ; i ++ )
200
+ // If '_pendingPostCodeData' is already set, then we are still in an on-going posting operation,
201
+ // waiting the code to be posted on the 'OnIdle' event.
202
+ if ( _pendingPostCodeData is not null || postCodeMessage . CodeBlocks . Count is 0 )
203
+ {
204
+ return ;
205
+ }
206
+
207
+ string codeToInsert ;
208
+ List < string > codeBlocks = postCodeMessage . CodeBlocks ;
209
+ List < PredictionCandidate > predictionCandidates = null ;
210
+
211
+ if ( codeBlocks . Count is 1 )
212
+ {
213
+ codeToInsert = codeBlocks [ 0 ] ;
214
+ }
215
+ else if ( Predictor . TryProcessForPrediction ( codeBlocks , out predictionCandidates ) )
216
+ {
217
+ codeToInsert = predictionCandidates [ 0 ] . Code ;
218
+ }
219
+ else
208
220
{
209
- if ( i > 0 )
221
+ // Use LF as line ending to be consistent with the response from LLM.
222
+ StringBuilder sb = new ( capacity : 50 ) ;
223
+ for ( int i = 0 ; i < codeBlocks . Count ; i ++ )
210
224
{
211
- sb . Append ( '\n ' ) ;
225
+ if ( i > 0 )
226
+ {
227
+ sb . Append ( '\n ' ) ;
228
+ }
229
+
230
+ sb . Append ( codeBlocks [ i ] ) . Append ( '\n ' ) ;
212
231
}
213
232
214
- sb . Append ( codeBlocks [ i ] ) . Append ( ' \n ' ) ;
233
+ codeToInsert = sb . ToString ( ) ;
215
234
}
216
235
217
- codeToInsert = sb . ToString ( ) ;
218
- }
219
-
220
- // When PSReadLine is actively running, 'TreatControlCAsInput' would be set to 'true' because
221
- // it handles 'Ctrl+c' as regular input.
222
- // When the value is 'false', it means PowerShell is still busy running scripts or commands.
223
- if ( Console . TreatControlCAsInput )
224
- {
225
- PSRLRevertLine ( ) ;
226
- PSRLInsert ( codeToInsert ) ;
227
- _predictor . SetCandidates ( predictionCandidates ) ;
236
+ // When PSReadLine is actively running, 'TreatControlCAsInput' would be set to 'true' because
237
+ // it handles 'Ctrl+c' as regular input.
238
+ // When the value is 'false', it means PowerShell is still busy running scripts or commands.
239
+ if ( Console . TreatControlCAsInput )
240
+ {
241
+ PSRLRevertLine ( ) ;
242
+ PSRLInsert ( codeToInsert ) ;
243
+ _predictor . SetCandidates ( predictionCandidates ) ;
244
+ }
245
+ else
246
+ {
247
+ _pendingPostCodeData = new CodePostData ( codeToInsert , predictionCandidates ) ;
248
+ // We use script block handler instead of a delegate handler because the latter will run
249
+ // in a background thread, while the former will run in the pipeline thread, which is way
250
+ // more predictable.
251
+ _runspace . Events . SubscribeEvent (
252
+ source : null ,
253
+ eventName : null ,
254
+ sourceIdentifier : PSEngineEvent . OnIdle ,
255
+ data : null ,
256
+ action : _onIdleAction ,
257
+ supportEvent : true ,
258
+ forwardEvent : false ,
259
+ maxTriggerCount : 1 ) ;
260
+ }
228
261
}
229
- else
262
+ finally
230
263
{
231
- _pendingPostCodeData = new CodePostData ( codeToInsert , predictionCandidates ) ;
232
- // We use script block handler instead of a delegate handler because the latter will run
233
- // in a background thread, while the former will run in the pipeline thread, which is way
234
- // more predictable.
235
- _runspace . Events . SubscribeEvent (
236
- source : null ,
237
- eventName : null ,
238
- sourceIdentifier : PSEngineEvent . OnIdle ,
239
- data : null ,
240
- action : _onIdleAction ,
241
- supportEvent : true ,
242
- forwardEvent : false ,
243
- maxTriggerCount : 1 ) ;
264
+ // Reset the state.
265
+ _processingCodePost = 0 ;
244
266
}
245
267
}
246
268
0 commit comments