66using Microsoft . PowerShell . EditorServices . Session ;
77using Microsoft . PowerShell . EditorServices . Transport . Stdio . Event ;
88using Microsoft . PowerShell . EditorServices . Transport . Stdio . Message ;
9+ using Microsoft . PowerShell . EditorServices . Utility ;
10+ using System ;
911using System . Collections . Generic ;
12+ using System . Threading ;
13+ using System . Threading . Tasks ;
1014
1115namespace Microsoft . PowerShell . EditorServices . Transport . Stdio . Request
1216{
1317 [ MessageTypeName ( "geterr" ) ]
1418 public class ErrorRequest : RequestBase < ErrorRequestArguments >
1519 {
20+ private static CancellationTokenSource existingRequestCancellation ;
21+
1622 public static ErrorRequest Create ( params string [ ] filePaths )
1723 {
1824 return new ErrorRequest
@@ -30,8 +36,78 @@ public override void ProcessMessage(
3036 {
3137 List < ScriptFile > fileList = new List < ScriptFile > ( ) ;
3238
39+ // If there's an existing task, attempt to cancel it
40+ try
41+ {
42+ if ( existingRequestCancellation != null )
43+ {
44+ // Try to cancel the request
45+ existingRequestCancellation . Cancel ( ) ;
46+
47+ // If cancellation didn't throw an exception,
48+ // clean up the existing token
49+ existingRequestCancellation . Dispose ( ) ;
50+ existingRequestCancellation = null ;
51+ }
52+ }
53+ catch ( Exception e )
54+ {
55+ // TODO: Catch a more specific exception!
56+ Logger . Write (
57+ LogLevel . Error ,
58+ string . Format (
59+ "Exception while cancelling analysis task:\n \n {0}" ,
60+ e . ToString ( ) ) ) ;
61+
62+ return ;
63+ }
64+
65+ // Create a fresh cancellation token and then start the task.
66+ // We create this on a different TaskScheduler so that we
67+ // don't block the main message loop thread.
68+ // TODO: Is there a better way to do this?
69+ existingRequestCancellation = new CancellationTokenSource ( ) ;
70+ Task . Factory . StartNew (
71+ ( ) =>
72+ DelayThenInvokeDiagnostics (
73+ this . Arguments . Delay ,
74+ this . Arguments . Files ,
75+ editorSession ,
76+ messageWriter ,
77+ existingRequestCancellation . Token ) ,
78+ CancellationToken . None ,
79+ TaskCreationOptions . None ,
80+ TaskScheduler . Default ) ;
81+ }
82+
83+ private static async Task DelayThenInvokeDiagnostics (
84+ int delayMilliseconds ,
85+ string [ ] filesToAnalyze ,
86+ EditorSession editorSession ,
87+ MessageWriter messageWriter ,
88+ CancellationToken cancellationToken )
89+ {
90+ // First of all, wait for the desired delay period before
91+ // analyzing the provided list of files
92+ try
93+ {
94+ await Task . Delay ( delayMilliseconds , cancellationToken ) ;
95+ }
96+ catch ( TaskCanceledException )
97+ {
98+ // If the task is cancelled, exit directly
99+ return ;
100+ }
101+
102+ // If we've made it past the delay period then we don't care
103+ // about the cancellation token anymore. This could happen
104+ // when the user stops typing for long enough that the delay
105+ // period ends but then starts typing while analysis is going
106+ // on. It makes sense to send back the results from the first
107+ // delay period while the second one is ticking away.
108+
33109 // Get the requested files
34- foreach ( string filePath in this . Arguments . Files )
110+ foreach ( string filePath in filesToAnalyze )
35111 {
36112 ScriptFile scriptFile =
37113 editorSession . Workspace . GetFile (
0 commit comments