6
6
using Microsoft . PowerShell . EditorServices . Session ;
7
7
using Microsoft . PowerShell . EditorServices . Transport . Stdio . Event ;
8
8
using Microsoft . PowerShell . EditorServices . Transport . Stdio . Message ;
9
+ using Microsoft . PowerShell . EditorServices . Utility ;
10
+ using System ;
9
11
using System . Collections . Generic ;
12
+ using System . Threading ;
13
+ using System . Threading . Tasks ;
10
14
11
15
namespace Microsoft . PowerShell . EditorServices . Transport . Stdio . Request
12
16
{
13
17
[ MessageTypeName ( "geterr" ) ]
14
18
public class ErrorRequest : RequestBase < ErrorRequestArguments >
15
19
{
20
+ private static CancellationTokenSource existingRequestCancellation ;
21
+
16
22
public static ErrorRequest Create ( params string [ ] filePaths )
17
23
{
18
24
return new ErrorRequest
@@ -30,8 +36,78 @@ public override void ProcessMessage(
30
36
{
31
37
List < ScriptFile > fileList = new List < ScriptFile > ( ) ;
32
38
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
+
33
109
// Get the requested files
34
- foreach ( string filePath in this . Arguments . Files )
110
+ foreach ( string filePath in filesToAnalyze )
35
111
{
36
112
ScriptFile scriptFile =
37
113
editorSession . Workspace . GetFile (
0 commit comments