Skip to content

Commit 0d8c389

Browse files
committed
Fix race conditions
1 parent b770198 commit 0d8c389

File tree

5 files changed

+63
-102
lines changed

5 files changed

+63
-102
lines changed

src/DiagnosticCollection.ts

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as vscode from 'vscode';
22
import * as vscodeOniguruma from 'vscode-oniguruma';
33
import { Node, QueryCapture } from 'web-tree-sitter';
4-
import { getLastNode, getTrees, parseEvents, queryNode, toRange, trees } from "./TreeSitter";
4+
import { getLastNode, getTrees, queryNode, toRange, trees } from "./TreeSitter";
55
import { closeEnoughQuestionMark, DocumentSelector, getPackageJSON, stringify, wagnerFischer } from "./extension";
66
import { unicodeproperties } from "./UNICODE_PROPERTIES";
77
import { ignoreDiagnosticsUnusedRepos } from "./Providers/CodeActionsProvider";
@@ -28,43 +28,76 @@ type OnigScanner = vscodeOniguruma.OnigScanner & {
2828
readonly _options: vscodeOniguruma.FindOption[];
2929
};
3030

31+
const activeDocuments: {
32+
[uriString: string]: {
33+
countDown: number;
34+
timeout: NodeJS.Timeout | number | undefined; // VSCode vs VSCode Web;
35+
};
36+
} = {};
3137

3238
const DiagnosticCollection = vscode.languages.createDiagnosticCollection("textmate");
3339
export function initDiagnostics(context: vscode.ExtensionContext) {
3440
// vscode.window.showInformationMessage(JSON.stringify("initDiagnostics"));
3541
context.subscriptions.push(DiagnosticCollection);
36-
parseEvents.push(Diagnostics);
3742

3843
for (const editor of vscode.window.visibleTextEditors) {
39-
// vscode.window.showInformationMessage(JSON.stringify("visible"));
40-
const document = editor.document;
41-
if (!vscode.languages.match(DocumentSelector, document)) {
42-
continue;
43-
}
44-
Diagnostics(document);
44+
// vscode.window.showInformationMessage(`visible\n${JSON.stringify(document)}`);
45+
debouncedDiagnostics(editor.document);
4546
}
4647

4748
context.subscriptions.push(
4849
vscode.workspace.onDidOpenTextDocument((document: vscode.TextDocument) => {
49-
// vscode.window.showInformationMessage(JSON.stringify("open"));
50-
if (!vscode.languages.match(DocumentSelector, document)) {
51-
return;
52-
}
53-
Diagnostics(document);
54-
})
55-
);
56-
57-
context.subscriptions.push(
50+
// vscode.window.showInformationMessage(`open\n${JSON.stringify(document)}`);
51+
debouncedDiagnostics(document);
52+
}),
53+
vscode.workspace.onDidChangeTextDocument((edits: vscode.TextDocumentChangeEvent) => {
54+
// vscode.window.showInformationMessage(`edit\n${JSON.stringify(edits)}`);
55+
debouncedDiagnostics(edits.document);
56+
}),
5857
vscode.workspace.onDidCloseTextDocument((document: vscode.TextDocument) => {
59-
// vscode.window.showInformationMessage(JSON.stringify("close"));
58+
// vscode.window.showInformationMessage(`close\n${JSON.stringify(document)}`);
59+
delete activeDocuments[document.uri.toString()];
6060
DiagnosticCollection.delete(document.uri);
6161
})
6262
);
6363
}
6464

65+
export function debouncedDiagnostics(document: vscode.TextDocument) {
66+
if (!vscode.languages.match(DocumentSelector, document)) {
67+
return;
68+
}
69+
70+
// https://github.com/microsoft/vscode/issues/11487
71+
const uriString = document.uri.toString();
72+
const activeDocument = activeDocuments[uriString] = activeDocuments[uriString] ?? {
73+
countDown: 0,
74+
timeout: undefined,
75+
};
76+
activeDocument.countDown++; // waits longer the more edits there are
77+
78+
// Debounce recently repeated requests
79+
if (activeDocument.timeout == undefined) {
80+
81+
// Wait 50ms and repeatedly execute CallBack
82+
activeDocument.timeout = setInterval(
83+
async () => {
84+
// setInterval() waits for current callback to finish
85+
86+
if (activeDocument.countDown < 0) {
87+
clearInterval(activeDocument.timeout); // timeout.refresh() doesn't work in VSCode web
88+
await Diagnostics(document);
89+
activeDocument.timeout = undefined;
90+
}
91+
92+
activeDocument.countDown -= 2;
93+
},
94+
50, // 50 milliseconds
95+
);
96+
}
97+
}
6598

66-
export async function Diagnostics(document: vscode.TextDocument) {
67-
// vscode.window.showInformationMessage("Diagnostics");
99+
async function Diagnostics(document: vscode.TextDocument) {
100+
// vscode.window.showInformationMessage(`Diagnostics${JSON.stringify(document)}`);
68101
// const start = performance.now();
69102

70103
const trees = getTrees(document);

src/Providers/CodeActionsProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Node, Tree } from 'web-tree-sitter';
33
import { getTrees, queryNode, toRange, trees } from "../TreeSitter";
44
import { unicodeproperties, UNICODE_PROPERTIES } from "../UNICODE_PROPERTIES";
55
import { closeEnoughQuestionMark, stringify, wagnerFischer } from "../extension";
6-
import { Diagnostics } from '../DiagnosticCollection';
6+
import { debouncedDiagnostics } from '../DiagnosticCollection';
77

88
export const metadata: vscode.CodeActionProviderMetadata = {
99
providedCodeActionKinds: [
@@ -195,7 +195,7 @@ export const CodeActionsProvider: vscode.CodeActionProvider = {
195195
break;
196196
case 'quickfix.ignore':
197197
ignoreDiagnosticsUnusedRepos = true;
198-
Diagnostics(document);
198+
debouncedDiagnostics(document);
199199
/* FALLTHROUGH */
200200
case 'quickfix':
201201
default:

src/Providers/CompletionItemProvider.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ export const CompletionItemProvider: vscode.CompletionItemProvider = {
6060
async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Promise<vscode.CompletionList<vscode.CompletionItem> | undefined> {
6161
// vscode.window.showInformationMessage(JSON.stringify("Completions"));
6262
// const start = performance.now();
63-
await sleep(50); // partially avoids race condition with reparseTextDocument()
6463

6564
const trees = getTrees(document);
6665
const tree = trees.jsonTree;

src/Providers/DocumentFormattingEditProvider.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ export const OnTypeFormattingEditProvider: vscode.OnTypeFormattingEditProvider =
7777
async provideOnTypeFormattingEdits(document: vscode.TextDocument, position: vscode.Position, ch: string, options: vscode.FormattingOptions, token: vscode.CancellationToken): Promise<vscode.TextEdit[] | undefined> {
7878
// vscode.window.showInformationMessage(JSON.stringify("FormatType"));
7979
// const start = performance.now();
80-
await sleep(50); // partially avoids race condition with reparseTextDocument()
8180

8281
const trees = getTrees(document);
8382
const jsonTree = trees.jsonTree;

src/TreeSitter.ts

Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function getTrees(source: vscode.TextDocument | vscode.Uri): trees {
2929
}
3030
}
3131

32-
vscode.window.showWarningMessage(`TextMate: TreeSitter Tree does not exist!\nFile:\n${JSON.stringify(source)}\nTrees:\n${JSON.stringify(trees)}`);
32+
vscode.window.showWarningMessage(`TextMate: TreeSitter Tree does not exist!\nFile:\n${JSON.stringify(source)}\nTrees:\n${JSON.stringify(trees, stringify)}`);
3333
return docTrees;
3434
}
3535

@@ -190,8 +190,6 @@ export function toPosition(point: TreeSitter.Point): vscode.Position {
190190
}
191191

192192

193-
export const parseEvents: ((document: vscode.TextDocument) => void)[] = [];
194-
195193
let jsonParser: TreeSitter.Parser;
196194
let regexParser: TreeSitter.Parser;
197195

@@ -224,98 +222,30 @@ export async function initTreeSitter(context: vscode.ExtensionContext) {
224222
const regexLanguage = await TreeSitter.Language.load(regexWasm);
225223
regexParser.setLanguage(regexLanguage);
226224

227-
const activeDocuments: {
228-
[uriString: string]: {
229-
edits: vscode.TextDocumentChangeEvent | undefined;
230-
timeout: NodeJS.Timeout | number | undefined; // VSCode vs VSCode Web
231-
// version: number;
232-
};
233-
} = {};
234-
235-
// for (const editor of vscode.window.visibleTextEditors) {
236-
// // vscode.window.showInformationMessage(JSON.stringify("visible"));
237-
// if (!vscode.languages.match(DocumentSelector, editor.document)) {
238-
// return;
239-
// }
240-
// parseTextDocument(editor.document);
241-
// }
242-
243225
for (const editor of vscode.window.visibleTextEditors) {
244-
// vscode.window.showInformationMessage(JSON.stringify("visible"));
245-
if (!vscode.languages.match(DocumentSelector, editor.document)) {
246-
continue;
247-
}
226+
// vscode.window.showInformationMessage(`visible\n${JSON.stringify(editor)}`);
248227
parseTextDocument(editor.document);
249228
}
250229

251230
context.subscriptions.push(
252231
vscode.workspace.onDidOpenTextDocument(document => {
253-
// vscode.window.showInformationMessage(JSON.stringify("open"));
254-
if (!vscode.languages.match(DocumentSelector, document)) {
255-
return;
256-
}
232+
// vscode.window.showInformationMessage(`open\n${JSON.stringify(document)}`);
257233
parseTextDocument(document);
258234
}),
259235

260236
vscode.workspace.onDidChangeTextDocument(edits => {
261-
// vscode.window.showInformationMessage(JSON.stringify("change"));
237+
// vscode.window.showInformationMessage(`edit\n${JSON.stringify(edits)}`);
262238
const document = edits.document;
263239
if (!vscode.languages.match(DocumentSelector, document)) {
264240
return;
265241
}
266242

267-
// https://github.com/microsoft/vscode/issues/11487
268-
const uriString = document.uri.toString();
269-
const activeDocument = activeDocuments[uriString] ?? { edits: null, timeout: null };
270-
activeDocuments[uriString] = activeDocument;
271-
272-
// Debounce recently repeated requests
273-
if (activeDocument.timeout == null) {
274-
// Run Diagnostics instantly on first edit
275-
reparseTextDocument(edits);
276-
277-
// Wait 50ms and execute CallBack regardless of if there are gonna be new edits or not
278-
activeDocument.timeout = setInterval(
279-
() => {
280-
if (activeDocument.edits == undefined) {
281-
// No new edits? exit.
282-
clearInterval(activeDocument.timeout); // timeout.refresh() doesn't work in VSCode web
283-
activeDocument.timeout = undefined;
284-
285-
for (const parseEvent of parseEvents) {
286-
try {
287-
parseEvent(document);
288-
} catch (error) { }
289-
}
290-
return;
291-
}
292-
293-
try {
294-
// setInterval() waits for current callback to finish
295-
reparseTextDocument(activeDocument.edits);
296-
} catch (error) {
297-
vscode.window.showInformationMessage(JSON.stringify(error));
298-
}
299-
activeDocument.edits = undefined;
300-
},
301-
50, // 50 millisecond intervals. Does anyone want this as a config?
302-
);
303-
return;
304-
}
305-
306-
// Add on the latest edits
307-
activeDocument.edits = {
308-
document: document,
309-
contentChanges: (activeDocument.edits?.contentChanges ?? []).concat(edits.contentChanges),
310-
reason: activeDocument.edits?.reason,
311-
};
312-
// vscode.window.showInformationMessage(JSON.stringify(activeDocument.edits));
243+
reparseTextDocument(edits);
313244
}),
314245

315246
vscode.workspace.onDidCloseTextDocument(document => {
316-
// vscode.window.showInformationMessage(JSON.stringify("close"));
247+
// vscode.window.showInformationMessage(`close\n${JSON.stringify(document)}`);
317248
const uriString = document.uri.toString();
318-
delete activeDocuments[uriString];
319249
if (trees[uriString]) {
320250
trees[uriString].jsonTree.delete();
321251
trees[uriString].regexTrees.clear();
@@ -327,7 +257,7 @@ export async function initTreeSitter(context: vscode.ExtensionContext) {
327257

328258

329259
function parseTextDocument(document: vscode.TextDocument) {
330-
// vscode.window.showInformationMessage(JSON.stringify("ParseTextDocument"));
260+
// vscode.window.showInformationMessage(`ParseTextDocument\n${JSON.stringify(document)}`);
331261
// const start = performance.now();
332262

333263
if (!vscode.languages.match(DocumentSelector, document)) {
@@ -377,7 +307,7 @@ function parseTextDocument(document: vscode.TextDocument) {
377307
}
378308

379309
function reparseTextDocument(edits: vscode.TextDocumentChangeEvent) {
380-
// vscode.window.showInformationMessage(JSON.stringify("ReparseTextDocument"));
310+
// vscode.window.showInformationMessage(`ReparseTextDocument\n${JSON.stringify(edits)}`);
381311
// const start = performance.now();
382312

383313
if (edits.contentChanges.length == 0) {

0 commit comments

Comments
 (0)