1919class DiagnosticsEngine
2020{
2121 /**
22- * @var Deferred<TextDocumentItem >
22+ * @var Deferred<null >
2323 */
2424 private Deferred $ deferred ;
2525
26- private bool $ running = false ;
27-
28- private ?TextDocumentItem $ next = null ;
26+ private ?TextDocumentItem $ waiting = null ;
2927
3028 /**
3129 * @var array<int|string,list<Diagnostic>>
@@ -42,6 +40,8 @@ class DiagnosticsEngine
4240 */
4341 private array $ locks = [];
4442
43+ private float $ lastUpdatedAt ;
44+
4545 /**
4646 * @param DiagnosticsProvider[] $providers
4747 */
@@ -51,6 +51,7 @@ public function __construct(private ClientApi $clientApi, private LoggerInterfac
5151 $ this ->providers = $ providers ;
5252 $ this ->clientApi = $ clientApi ;
5353 $ this ->sleepTime = $ sleepTime ;
54+ $ this ->lastUpdatedAt = 0.0 ;
5455 }
5556
5657 public function clear (TextDocumentItem $ textDocument ): void
@@ -67,8 +68,6 @@ public function clear(TextDocumentItem $textDocument): void
6768 */
6869 public function run (CancellationToken $ token ): Promise
6970 {
70-
71-
7271 return \Amp \call (function () use ($ token ) {
7372 while (true ) {
7473 try {
@@ -77,30 +76,29 @@ public function run(CancellationToken $token): Promise
7776 return ;
7877 }
7978
80- $ textDocument = yield $ this ->nextDocument ();
81- $ lastKnownVersion = ($ this ->versions [$ textDocument ->uri ] ?? -1 );
79+ yield $ this ->awaitNextDocument ();
8280
83- if ($ lastKnownVersion <= $ textDocument ->version && isset ($ this ->diagnostics [$ textDocument ->uri ])) {
84- // reset diagnostics for this document
85- $ this ->clientApi ->diagnostics ()->publishDiagnostics (
86- $ textDocument ->uri ,
87- $ textDocument ->version ,
88- [],
89- );
90- }
81+ $ gracePeriod = abs ($ this ->sleepTime - ((microtime (true ) - $ this ->lastUpdatedAt ) * 1000 ));
82+ yield delay (intval ($ gracePeriod ));
9183
92- $ this ->diagnostics [$ textDocument ->uri ] = [];
84+ $ textDocument = $ this ->waiting ;
85+ $ this ->waiting = null ;
86+ // allow the next document update to resolve
9387 $ this ->deferred = new Deferred ();
94- // after we have reset deferred, we can safely set linting to
95- // `false` and let another resolve happen
96- $ this ->running = false ;
9788
98- // if the last processed version of the document is more recent
99- // than the last then continue.
100- if ($ lastKnownVersion >= $ textDocument ->version ) {
89+ // should never happen
90+ if ($ textDocument === null ) {
10191 continue ;
10292 }
10393
94+ // reset diagnostics for this document
95+ $ this ->clientApi ->diagnostics ()->publishDiagnostics (
96+ $ textDocument ->uri ,
97+ $ textDocument ->version ,
98+ [],
99+ );
100+ $ this ->diagnostics [$ textDocument ->uri ] = [];
101+
104102 $ this ->versions [$ textDocument ->uri ] = $ textDocument ->version ;
105103
106104 $ crashedProviders = [];
@@ -113,7 +111,7 @@ public function run(CancellationToken $token): Promise
113111 asyncCall (function () use ($ providerId , $ provider , $ token , $ textDocument , &$ crashedProviders ) {
114112 $ start = microtime (true );
115113
116- yield $ this ->await ($ providerId );
114+ yield $ this ->awaitProviderLock ($ providerId );
117115
118116 if (!$ this ->isDocumentCurrent ($ textDocument )) {
119117 return ;
@@ -151,16 +149,11 @@ public function run(CancellationToken $token): Promise
151149 $ diagnostics
152150 );
153151
154- $ timeToSleep = $ this ->sleepTime - $ elapsed ;
155-
156- if ($ timeToSleep > 0 ) {
157- yield delay ($ timeToSleep );
158- }
159-
160152 if (!$ this ->isDocumentCurrent ($ textDocument )) {
161153 return ;
162154 }
163155
156+
164157 $ this ->clientApi ->diagnostics ()->publishDiagnostics (
165158 $ textDocument ->uri ,
166159 $ textDocument ->version ,
@@ -174,27 +167,35 @@ public function run(CancellationToken $token): Promise
174167
175168 public function enqueue (TextDocumentItem $ textDocument ): void
176169 {
177- // if we are already linting then store whatever comes afterwards in
178- // next, overwriting the redundant update
179- if ($ this ->running === true ) {
180- $ this ->next = $ textDocument ;
170+ // set the last updated at timestamp - this will be used as the basis of
171+ // the grace period before linting
172+ $ this ->lastUpdatedAt = microtime (true );
173+
174+ $ waiting = $ this ->waiting ;
175+
176+ // set the next document
177+ $ this ->waiting = $ textDocument ;
178+
179+ // if we already had a waiting document then do nothing
180+ // it will get resolved next time
181+ if ($ waiting !== null ) {
181182 return ;
182183 }
183184
184- // resolving the promise will start the diagnostc resolving
185- $ this ->running = true ;
186- $ this ->deferred ->resolve ($ textDocument );
185+ // otherwise trigger the lint process
186+ $ this ->deferred ->resolve ();
187187 }
188188
189189 private function isDocumentCurrent (TextDocumentItem $ textDocument ): bool
190190 {
191191 return $ textDocument ->version === ($ this ->versions [$ textDocument ->uri ] ?? -1 );
192192 }
193+
193194 /**
194195 * @param string|int $providerId
195196 * @return Promise<bool>
196197 */
197- private function await ($ providerId ): Promise
198+ private function awaitProviderLock ($ providerId ): Promise
198199 {
199200 if (!array_key_exists ($ providerId , $ this ->locks )) {
200201 return new Success (true );
@@ -205,14 +206,12 @@ private function await($providerId): Promise
205206 }
206207
207208 /**
208- * @return Promise<TextDocumentItem >
209+ * @return Promise<null >
209210 */
210- private function nextDocument (): Promise
211+ private function awaitNextDocument (): Promise
211212 {
212- if ($ this ->next ) {
213- $ textDocument = $ this ->next ;
214- $ this ->next = null ;
215- return new Success ($ textDocument );
213+ if ($ this ->waiting ) {
214+ return new Success ();
216215 }
217216
218217 return $ this ->deferred ->promise ();
0 commit comments