Skip to content

Commit 9e11a40

Browse files
committed
Batch TreeSitter incremental changes along with Diagnostics
1 parent c489e34 commit 9e11a40

File tree

4 files changed

+211
-145
lines changed

4 files changed

+211
-145
lines changed

src/DiagnosticCollection.ts

Lines changed: 88 additions & 79 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';
3-
import { getTrees, jsonParserLanguage, queryNode, toPosition, toRange, trueParent } from "./TreeSitter";
4-
import { DocumentSelector } from "./extension";
3+
import { getTrees, parseEvents, queryNode, toPosition, toRange, trueParent } from "./TreeSitter";
4+
import { DocumentSelector, stringify } from "./extension";
55
import { unicodeproperties } from "./UNICODE_PROPERTIES";
66

77

@@ -26,90 +26,97 @@ type OnigScanner = vscodeOniguruma.OnigScanner & {
2626
readonly _options: vscodeOniguruma.FindOption[];
2727
};
2828

29-
29+
const DiagnosticCollection = vscode.languages.createDiagnosticCollection("textmate");
3030
export function initDiagnostics(context: vscode.ExtensionContext) {
3131
// vscode.window.showInformationMessage(JSON.stringify("initDiagnostics"));
32-
const DiagnosticCollection = vscode.languages.createDiagnosticCollection("textmate");
3332
context.subscriptions.push(DiagnosticCollection);
33+
parseEvents.push(Diagnostics);
3434

35-
const activeDocuments: {
36-
[uriString: string]: {
37-
edits: vscode.TextDocumentChangeEvent;
38-
timeout: NodeJS.Timeout;
39-
};
40-
} = {};
35+
// const activeDocuments: {
36+
// [uriString: string]: {
37+
// edits: vscode.TextDocumentChangeEvent;
38+
// timeout: NodeJS.Timeout | number;
39+
// };
40+
// } = {};
4141

4242
for (const editor of vscode.window.visibleTextEditors) {
4343
// vscode.window.showInformationMessage(JSON.stringify("visible"));
44-
const uriString = editor.document.uri.toString();
45-
activeDocuments[uriString] = { edits: null, timeout: null };
46-
Diagnostics(editor.document, DiagnosticCollection);
44+
const document = editor.document;
45+
if (!vscode.languages.match(DocumentSelector, document)) {
46+
continue;
47+
}
48+
// const uriString = document.uri.toString();
49+
// activeDocuments[uriString] = { edits: null, timeout: null };
50+
Diagnostics(document);
4751
}
4852

49-
context.subscriptions.push(
50-
vscode.workspace.onDidOpenTextDocument((document: vscode.TextDocument) => {
51-
// vscode.window.showInformationMessage(JSON.stringify("open"));
52-
const uriString = document.uri.toString();
53-
activeDocuments[uriString] = { edits: null, timeout: null };
54-
Diagnostics(document, DiagnosticCollection);
55-
})
56-
);
57-
58-
context.subscriptions.push(
59-
vscode.workspace.onDidChangeTextDocument((edits: vscode.TextDocumentChangeEvent) => {
60-
// vscode.window.showInformationMessage(JSON.stringify("change"));
61-
62-
if (edits.contentChanges.length == 0) {
63-
// File saving triggers onDidChangeTextDocument()
64-
return;
65-
}
66-
67-
const uriString = edits.document.uri.toString();
68-
const activeDocument = activeDocuments[uriString];
69-
70-
// Debounce recently repeated requests
71-
if (activeDocument.timeout == null) {
72-
// Run Diagnostics instantly on first edit
73-
Diagnostics(edits.document, DiagnosticCollection);
74-
75-
// Wait 50ms and execute CallBack reguardless of if there were new edits or not
76-
activeDocument.timeout = setTimeout(
77-
() => {
78-
if (activeDocument.edits == null) {
79-
// No new edits? exit.
80-
activeDocument.timeout = null;
81-
return;
82-
}
83-
84-
// Wait for slow Diagnostics first before rescheduling Timer CallBack
85-
Diagnostics(activeDocument.edits.document, DiagnosticCollection);
86-
activeDocument.timeout.refresh(); // Setting Timeouts within Timeouts :)
87-
activeDocument.edits = null;
88-
},
89-
50, // 50 milisecond intervals. Does anyone want this as a config?
90-
);
91-
return;
92-
}
93-
94-
// Use the latest edits
95-
activeDocument.edits = edits;
96-
})
97-
);
98-
99-
context.subscriptions.push(
100-
vscode.workspace.onDidCloseTextDocument((document: vscode.TextDocument) => {
101-
// vscode.window.showInformationMessage(JSON.stringify("close"));
102-
const uriString = document.uri.toString();
103-
delete activeDocuments[uriString];
104-
DiagnosticCollection.delete(document.uri);
105-
})
106-
);
53+
// context.subscriptions.push(
54+
// vscode.workspace.onDidOpenTextDocument((document: vscode.TextDocument) => {
55+
// // vscode.window.showInformationMessage(JSON.stringify("open"));
56+
// if (!vscode.languages.match(DocumentSelector, document)) {
57+
// return;
58+
// }
59+
// const uriString = document.uri.toString();
60+
// activeDocuments[uriString] = { edits: null, timeout: null };
61+
// Diagnostics(document, DiagnosticCollection);
62+
// })
63+
// );
64+
65+
// context.subscriptions.push(
66+
// vscode.workspace.onDidChangeTextDocument((edits: vscode.TextDocumentChangeEvent) => {
67+
// // vscode.window.showInformationMessage(JSON.stringify("change"));
68+
// if (!vscode.languages.match(DocumentSelector, edits.document)) {
69+
// return;
70+
// }
71+
72+
// if (edits.contentChanges.length == 0) {
73+
// // File saving triggers onDidChangeTextDocument()
74+
// return;
75+
// }
76+
77+
// const uriString = edits.document.uri.toString();
78+
// const activeDocument = activeDocuments[uriString];
79+
80+
// // Debounce recently repeated requests
81+
// if (activeDocument.timeout == null) {
82+
// // Run Diagnostics instantly on first edit
83+
// Diagnostics(edits.document, DiagnosticCollection);
84+
85+
// // Wait 50ms and execute CallBack reguardless of if there were new edits or not
86+
// activeDocument.timeout = setInterval(
87+
// () => {
88+
// if (activeDocument.edits == null) {
89+
// // No new edits? exit.
90+
// clearInterval(activeDocument.timeout); // Works in VSCode web
91+
// activeDocument.timeout = null;
92+
// return;
93+
// }
94+
95+
// // setInterval() waits for current callback to finish
96+
// Diagnostics(activeDocument.edits.document, DiagnosticCollection);
97+
// activeDocument.edits = null;
98+
// },
99+
// 50, // 50 milisecond intervals. Does anyone want this as a config?
100+
// );
101+
// return;
102+
// }
103+
104+
// // Use the latest edits
105+
// activeDocument.edits = edits;
106+
// })
107+
// );
108+
109+
// context.subscriptions.push(
110+
// vscode.workspace.onDidCloseTextDocument((document: vscode.TextDocument) => {
111+
// // vscode.window.showInformationMessage(JSON.stringify("close"));
112+
// const uriString = document.uri.toString();
113+
// delete activeDocuments[uriString];
114+
// DiagnosticCollection.delete(document.uri);
115+
// })
116+
// );
107117
}
108118

109-
function Diagnostics(document: vscode.TextDocument, Diagnostics: vscode.DiagnosticCollection) {
110-
if (!vscode.languages.match(DocumentSelector, document)) {
111-
return;
112-
}
119+
function Diagnostics(document: vscode.TextDocument) {
113120
// vscode.window.showInformationMessage("Diagnostics");
114121
// const start = performance.now();
115122

@@ -293,6 +300,7 @@ function Diagnostics(document: vscode.TextDocument, Diagnostics: vscode.Diagnost
293300

294301
if (true) { // Oniguruma Regex errors. https://github.com/kkos/oniguruma
295302
// vscode.window.showInformationMessage(JSON.stringify("diagnostics Regex Oniguruma"));
303+
// const start = performance.now();
296304
const regexNodes = trees.regexNodes;
297305

298306
for (const id in regexNodes) {
@@ -331,6 +339,7 @@ function Diagnostics(document: vscode.TextDocument, Diagnostics: vscode.Diagnost
331339
diagnostics.push(diagnostic);
332340
}
333341
}
342+
// vscode.window.showInformationMessage(performance.now() - start + "ms");
334343
}
335344

336345
if (true) { // missing `#include`
@@ -340,7 +349,7 @@ function Diagnostics(document: vscode.TextDocument, Diagnostics: vscode.Diagnost
340349
let prevParent;
341350
let errorCount;
342351

343-
const repoQueryString = `
352+
const repoQueryString = `;scm
344353
(repo
345354
[(include) (patterns)] (repository
346355
(repo
@@ -530,14 +539,14 @@ function Diagnostics(document: vscode.TextDocument, Diagnostics: vscode.Diagnost
530539

531540
if (false) { // create artificial lag
532541
const start = performance.now();
533-
for (let i = 0; i < 200; i++) {
542+
for (let i = 0; i < 1000; i++) {
534543
queryNode(rootNode, `(ERROR) @ERROR`);
535544
}
536-
vscode.window.showInformationMessage(performance.now() - start + "ms");
545+
// vscode.window.showInformationMessage(performance.now() - start + "ms");
537546
}
538547

539548
// vscode.window.showInformationMessage(JSON.stringify(diagnostics));
540-
Diagnostics.set(document.uri, diagnostics);
549+
DiagnosticCollection.set(document.uri, diagnostics);
541550
// vscode.window.showInformationMessage(`Diagnostics ${performance.now() - start}ms`);
542551
}
543552

src/Providers/DefinitionProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as vscode from 'vscode';
2-
import { getTrees, getTree, toRange, toPoint, queryNode, trees, getLastNode } from "../TreeSitter";
2+
import { getTrees, toRange, toPoint, queryNode, trees, getLastNode } from "../TreeSitter";
33
import { DocumentSelector, stringify } from "../extension";
44
import { Point, SyntaxNode } from 'web-tree-sitter';
55

@@ -187,7 +187,7 @@ export const DefinitionProvider: vscode.DefinitionProvider = {
187187
if (!vscode.languages.match(DocumentSelector, textDocument)) {
188188
continue;
189189
}
190-
const documentTree = getTree(textDocument);
190+
const documentTree = getTrees(textDocument).jsonTree;
191191
queryString = `(json (scopeName (value) @scopeName (.eq? @scopeName "${scopeName}")))`;
192192
const documentScopeNameNode = queryNode(documentTree.rootNode, queryString).pop()?.node;
193193
if (documentScopeNameNode) {

src/Providers/ReferenceProvider.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import * as vscode from 'vscode';
2-
import { getTree, toRange, toPoint, queryNode } from "../TreeSitter";
2+
import { getTrees, toRange, toPoint, queryNode } from "../TreeSitter";
33

44

55
export const ReferenceProvider: vscode.ReferenceProvider = {
66
provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): vscode.Location[] {
77
// vscode.window.showInformationMessage(JSON.stringify("references"));
8-
const tree = getTree(document);
8+
const tree = getTrees(document).jsonTree;
99
const point = toPoint(position);
1010
let queryString: string;
1111
// vscode.window.showInformationMessage(JSON.stringify(tree.rootNode.namedDescendantForPosition(point).text));
1212

13-
queryString = `
13+
queryString = `;scm
1414
;(json (scopeName (value) @scopeName))
1515
(include (value) @include)
1616
(repo (key) @repo)
17-
`
17+
`;
1818
const referenceQueryCapture = queryNode(tree.rootNode, queryString, point);
1919
if (referenceQueryCapture == null) {
2020
return;
@@ -91,7 +91,7 @@ export const ReferenceProvider: vscode.ReferenceProvider = {
9191
else if (node.childForFieldName('self') || (scopeName && !ruleName)) { // $self
9292
// vscode.window.showInformationMessage("$self");
9393
queryString =
94-
`(include (value) @include (#match? @include "^#?\\\\$self$"))`
94+
`(include (value) @include (#match? @include "^#?\\\\$self$"))`;
9595
if (rootScopeName) {
9696
queryString +=
9797
`(include (value) @include (#eq? @include "${rootScopeName}"))` +
@@ -150,4 +150,4 @@ export const ReferenceProvider: vscode.ReferenceProvider = {
150150
// vscode.window.showInformationMessage(JSON.stringify(locations));
151151
return locations;
152152
}
153-
}
153+
};

0 commit comments

Comments
 (0)