aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/js/xtext/UpdateService.ts
diff options
context:
space:
mode:
Diffstat (limited to 'language-web/src/main/js/xtext/UpdateService.ts')
-rw-r--r--language-web/src/main/js/xtext/UpdateService.ts52
1 files changed, 13 insertions, 39 deletions
diff --git a/language-web/src/main/js/xtext/UpdateService.ts b/language-web/src/main/js/xtext/UpdateService.ts
index 3ab1daf9..838f9d5b 100644
--- a/language-web/src/main/js/xtext/UpdateService.ts
+++ b/language-web/src/main/js/xtext/UpdateService.ts
@@ -7,8 +7,8 @@ import { nanoid } from 'nanoid';
7 7
8import type { EditorStore } from '../editor/EditorStore'; 8import type { EditorStore } from '../editor/EditorStore';
9import type { XtextWebSocketClient } from './XtextWebSocketClient'; 9import type { XtextWebSocketClient } from './XtextWebSocketClient';
10import { ConditionVariable } from '../utils/ConditionVariable';
10import { getLogger } from '../utils/logger'; 11import { getLogger } from '../utils/logger';
11import { PendingTask } from '../utils/PendingTask';
12import { Timer } from '../utils/Timer'; 12import { Timer } from '../utils/Timer';
13import { 13import {
14 IContentAssistEntry, 14 IContentAssistEntry,
@@ -40,7 +40,10 @@ export class UpdateService {
40 40
41 private webSocketClient: XtextWebSocketClient; 41 private webSocketClient: XtextWebSocketClient;
42 42
43 private updateListeners: PendingTask<void>[] = []; 43 private updatedCondition = new ConditionVariable(
44 () => this.pendingUpdate === null && this.xtextStateId !== null,
45 WAIT_FOR_UPDATE_TIMEOUT_MS,
46 );
44 47
45 private idleUpdateTimer = new Timer(() => { 48 private idleUpdateTimer = new Timer(() => {
46 this.handleIdleUpdate(); 49 this.handleIdleUpdate();
@@ -59,9 +62,8 @@ export class UpdateService {
59 } 62 }
60 63
61 onTransaction(transaction: Transaction): void { 64 onTransaction(transaction: Transaction): void {
62 const { changes } = transaction; 65 if (transaction.docChanged) {
63 if (!changes.empty) { 66 this.dirtyChanges = this.dirtyChanges.composeDesc(transaction.changes.desc);
64 this.dirtyChanges = this.dirtyChanges.composeDesc(changes.desc);
65 this.idleUpdateTimer.reschedule(); 67 this.idleUpdateTimer.reschedule();
66 } 68 }
67 } 69 }
@@ -221,13 +223,7 @@ export class UpdateService {
221 [newStateId, result] = await callback(); 223 [newStateId, result] = await callback();
222 this.xtextStateId = newStateId; 224 this.xtextStateId = newStateId;
223 this.pendingUpdate = null; 225 this.pendingUpdate = null;
224 // Copy `updateListeners` so that we don't get into a race condition 226 this.updatedCondition.notifyAll();
225 // if one of the listeners adds another listener.
226 const listeners = this.updateListeners;
227 this.updateListeners = [];
228 listeners.forEach((listener) => {
229 listener.resolve();
230 });
231 return result; 227 return result;
232 } catch (e) { 228 } catch (e) {
233 log.error('Error while update', e); 229 log.error('Error while update', e);
@@ -238,39 +234,17 @@ export class UpdateService {
238 } 234 }
239 this.pendingUpdate = null; 235 this.pendingUpdate = null;
240 this.webSocketClient.forceReconnectOnError(); 236 this.webSocketClient.forceReconnectOnError();
241 const listeners = this.updateListeners; 237 this.updatedCondition.rejectAll(e);
242 this.updateListeners = [];
243 listeners.forEach((listener) => {
244 listener.reject(e);
245 });
246 throw e; 238 throw e;
247 } 239 }
248 } 240 }
249 241
250 private async prepareForDeltaUpdate() { 242 private async prepareForDeltaUpdate() {
251 if (this.pendingUpdate === null) { 243 // If no update is pending, but the full text hasn't been uploaded to the server yet,
252 if (this.xtextStateId === null) { 244 // we must start a full text upload.
253 return; 245 if (this.pendingUpdate === null && this.xtextStateId === null) {
254 }
255 await this.updateFullText(); 246 await this.updateFullText();
256 } 247 }
257 let nowMs = Date.now(); 248 await this.updatedCondition.waitFor();
258 const endMs = nowMs + WAIT_FOR_UPDATE_TIMEOUT_MS;
259 while (this.pendingUpdate !== null && nowMs < endMs) {
260 const timeoutMs = endMs - nowMs;
261 const promise = new Promise((resolve, reject) => {
262 const task = new PendingTask(resolve, reject, timeoutMs);
263 this.updateListeners.push(task);
264 });
265 // We must keep waiting uptil the update has completed,
266 // so the tasks can't be started in parallel.
267 // eslint-disable-next-line no-await-in-loop
268 await promise;
269 nowMs = Date.now();
270 }
271 if (this.pendingUpdate !== null || this.xtextStateId === null) {
272 log.error('No successful update in', WAIT_FOR_UPDATE_TIMEOUT_MS, 'ms');
273 throw new Error('Failed to wait for successful update');
274 }
275 } 249 }
276} 250}