Skip to content

Commit 3f198e6

Browse files
committed
Adding cancellation token checks for lower priority tasks
1 parent 8a5bebe commit 3f198e6

File tree

4 files changed

+210
-120
lines changed

4 files changed

+210
-120
lines changed

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3460,6 +3460,88 @@ namespace ts.projectSystem {
34603460
return JSON.parse(server.extractMessage(host.getOutput()[n]));
34613461
}
34623462
});
3463+
it("Lower priority tasks are cancellable", () => {
3464+
const f1 = {
3465+
path: "/a/app.ts",
3466+
content: `{ let x = 1; } var foo = "foo"; var bar = "bar"; var fooBar = "fooBar";`
3467+
};
3468+
const config = {
3469+
path: "/a/tsconfig.json",
3470+
content: JSON.stringify({
3471+
compilerOptions: {}
3472+
})
3473+
};
3474+
3475+
let requestToCancel = -1;
3476+
let isCancellationRequestedCount = 0;
3477+
let cancelAfterRequest = 3;
3478+
let operationCanceledExceptionThrown = false;
3479+
const cancellationToken: server.ServerCancellationToken = (function () {
3480+
let currentId: number;
3481+
return <server.ServerCancellationToken>{
3482+
setRequest(requestId) {
3483+
currentId = requestId;
3484+
},
3485+
resetRequest(requestId) {
3486+
assert.equal(requestId, currentId, "unexpected request id in cancellation")
3487+
currentId = undefined;
3488+
},
3489+
isCancellationRequested() {
3490+
isCancellationRequestedCount++;
3491+
return requestToCancel === currentId && isCancellationRequestedCount >= cancelAfterRequest;
3492+
}
3493+
}
3494+
})();
3495+
const host = createServerHost([f1, config]);
3496+
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken);
3497+
{
3498+
session.executeCommandSeq(<protocol.OpenRequest>{
3499+
command: "open",
3500+
arguments: { file: f1.path }
3501+
});
3502+
3503+
// send navbar request (normal priority)
3504+
session.executeCommandSeq(<protocol.NavBarRequest>{
3505+
command: "navbar",
3506+
arguments: { file: f1.path }
3507+
});
3508+
3509+
// ensure the nav bar request can be canceled
3510+
verifyExecuteCommandSeqIsCancellable(<protocol.NavBarRequest>{
3511+
command: "navbar",
3512+
arguments: { file: f1.path }
3513+
});
3514+
3515+
// send outlining spans request (normal priority)
3516+
session.executeCommandSeq(<protocol.OutliningSpansRequest>{
3517+
command: "outliningSpans",
3518+
arguments: { file: f1.path }
3519+
});
3520+
3521+
// ensure the outlining spans request can be canceled
3522+
verifyExecuteCommandSeqIsCancellable(<protocol.OutliningSpansRequest>{
3523+
command: "outliningSpans",
3524+
arguments: { file: f1.path }
3525+
});
3526+
}
3527+
3528+
function verifyExecuteCommandSeqIsCancellable<T extends server.protocol.Request>(request: Partial<T>) {
3529+
// Set the next request to be cancellable
3530+
// The cancellation token will cancel the request the third time
3531+
// isCancellationRequested() is called.
3532+
requestToCancel = session.getNextSeq();
3533+
isCancellationRequestedCount = 0;
3534+
operationCanceledExceptionThrown = false;
3535+
3536+
try {
3537+
session.executeCommandSeq(request);
3538+
} catch (e) {
3539+
assert(e instanceof OperationCanceledException);
3540+
operationCanceledExceptionThrown = true;
3541+
}
3542+
assert(operationCanceledExceptionThrown);
3543+
}
3544+
});
34633545
});
34643546

34653547
describe("maxNodeModuleJsDepth for inferred projects", () => {

src/services/navigationBar.ts

Lines changed: 122 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ namespace ts.NavigationBar {
1414
indent: number; // # of parents
1515
}
1616

17-
export function getNavigationBarItems(sourceFile: SourceFile): NavigationBarItem[] {
17+
export function getNavigationBarItems(sourceFile: SourceFile, cancellationToken: CancellationToken): NavigationBarItem[] {
1818
curSourceFile = sourceFile;
19-
const result = map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem);
19+
const result = map(topLevelItems(rootNavigationBarNode(sourceFile, cancellationToken)), convertToTopLevelItem);
2020
curSourceFile = undefined;
2121
return result;
2222
}
2323

24-
export function getNavigationTree(sourceFile: SourceFile): NavigationTree {
24+
export function getNavigationTree(sourceFile: SourceFile, cancellationToken: CancellationToken): NavigationTree {
2525
curSourceFile = sourceFile;
26-
const result = convertToTree(rootNavigationBarNode(sourceFile));
26+
const result = convertToTree(rootNavigationBarNode(sourceFile, cancellationToken));
2727
curSourceFile = undefined;
2828
return result;
2929
}
@@ -55,12 +55,12 @@ namespace ts.NavigationBar {
5555
const parentsStack: NavigationBarNode[] = [];
5656
let parent: NavigationBarNode;
5757

58-
function rootNavigationBarNode(sourceFile: SourceFile): NavigationBarNode {
58+
function rootNavigationBarNode(sourceFile: SourceFile, cancellationToken: CancellationToken): NavigationBarNode {
5959
Debug.assert(!parentsStack.length);
6060
const root: NavigationBarNode = { node: sourceFile, additionalNodes: undefined, parent: undefined, children: undefined, indent: 0 };
6161
parent = root;
6262
for (const statement of sourceFile.statements) {
63-
addChildrenRecursively(statement);
63+
addChildrenRecursively(statement, cancellationToken);
6464
}
6565
endNode();
6666
Debug.assert(!parent && !parentsStack.length);
@@ -103,138 +103,144 @@ namespace ts.NavigationBar {
103103
parent = parentsStack.pop();
104104
}
105105

106-
function addNodeWithRecursiveChild(node: Node, child: Node): void {
106+
function addNodeWithRecursiveChild(node: Node, child: Node, addChildrenRecursively: (node: Node) => void): void {
107107
startNode(node);
108108
addChildrenRecursively(child);
109109
endNode();
110110
}
111111

112112
/** Look for navigation bar items in node's subtree, adding them to the current `parent`. */
113-
function addChildrenRecursively(node: Node): void {
114-
if (!node || isToken(node)) {
115-
return;
116-
}
113+
function addChildrenRecursively(node: Node, cancellationToken: CancellationToken): void {
114+
function addChildrenRecursively(node: Node): void {
115+
cancellationToken.throwIfCancellationRequested();
117116

118-
switch (node.kind) {
119-
case SyntaxKind.Constructor:
120-
// Get parameter properties, and treat them as being on the *same* level as the constructor, not under it.
121-
const ctr = <ConstructorDeclaration>node;
122-
addNodeWithRecursiveChild(ctr, ctr.body);
123-
124-
// Parameter properties are children of the class, not the constructor.
125-
for (const param of ctr.parameters) {
126-
if (isParameterPropertyDeclaration(param)) {
127-
addLeafNode(param);
117+
if (!node || isToken(node)) {
118+
return;
119+
}
120+
121+
switch (node.kind) {
122+
case SyntaxKind.Constructor:
123+
// Get parameter properties, and treat them as being on the *same* level as the constructor, not under it.
124+
const ctr = <ConstructorDeclaration>node;
125+
addNodeWithRecursiveChild(ctr, ctr.body, addChildrenRecursively);
126+
127+
// Parameter properties are children of the class, not the constructor.
128+
for (const param of ctr.parameters) {
129+
if (isParameterPropertyDeclaration(param)) {
130+
addLeafNode(param);
131+
}
128132
}
129-
}
130-
break;
131-
132-
case SyntaxKind.MethodDeclaration:
133-
case SyntaxKind.GetAccessor:
134-
case SyntaxKind.SetAccessor:
135-
case SyntaxKind.MethodSignature:
136-
if (!hasDynamicName((<ClassElement | TypeElement>node))) {
137-
addNodeWithRecursiveChild(node, (<FunctionLikeDeclaration>node).body);
138-
}
139-
break;
133+
break;
140134

141-
case SyntaxKind.PropertyDeclaration:
142-
case SyntaxKind.PropertySignature:
143-
if (!hasDynamicName((<ClassElement | TypeElement>node))) {
144-
addLeafNode(node);
145-
}
146-
break;
147-
148-
case SyntaxKind.ImportClause:
149-
const importClause = <ImportClause>node;
150-
// Handle default import case e.g.:
151-
// import d from "mod";
152-
if (importClause.name) {
153-
addLeafNode(importClause);
154-
}
135+
case SyntaxKind.MethodDeclaration:
136+
case SyntaxKind.GetAccessor:
137+
case SyntaxKind.SetAccessor:
138+
case SyntaxKind.MethodSignature:
139+
if (!hasDynamicName((<ClassElement | TypeElement>node))) {
140+
addNodeWithRecursiveChild(node, (<FunctionLikeDeclaration>node).body, addChildrenRecursively);
141+
}
142+
break;
155143

156-
// Handle named bindings in imports e.g.:
157-
// import * as NS from "mod";
158-
// import {a, b as B} from "mod";
159-
const {namedBindings} = importClause;
160-
if (namedBindings) {
161-
if (namedBindings.kind === SyntaxKind.NamespaceImport) {
162-
addLeafNode(<NamespaceImport>namedBindings);
144+
case SyntaxKind.PropertyDeclaration:
145+
case SyntaxKind.PropertySignature:
146+
if (!hasDynamicName((<ClassElement | TypeElement>node))) {
147+
addLeafNode(node);
163148
}
164-
else {
165-
for (const element of (<NamedImports>namedBindings).elements) {
166-
addLeafNode(element);
149+
break;
150+
151+
case SyntaxKind.ImportClause:
152+
const importClause = <ImportClause>node;
153+
// Handle default import case e.g.:
154+
// import d from "mod";
155+
if (importClause.name) {
156+
addLeafNode(importClause);
157+
}
158+
159+
// Handle named bindings in imports e.g.:
160+
// import * as NS from "mod";
161+
// import {a, b as B} from "mod";
162+
const {namedBindings} = importClause;
163+
if (namedBindings) {
164+
if (namedBindings.kind === SyntaxKind.NamespaceImport) {
165+
addLeafNode(<NamespaceImport>namedBindings);
166+
}
167+
else {
168+
for (const element of (<NamedImports>namedBindings).elements) {
169+
addLeafNode(element);
170+
}
167171
}
168172
}
169-
}
170-
break;
171-
172-
case SyntaxKind.BindingElement:
173-
case SyntaxKind.VariableDeclaration:
174-
const decl = <VariableDeclaration>node;
175-
const name = decl.name;
176-
if (isBindingPattern(name)) {
177-
addChildrenRecursively(name);
178-
}
179-
else if (decl.initializer && isFunctionOrClassExpression(decl.initializer)) {
180-
// For `const x = function() {}`, just use the function node, not the const.
181-
addChildrenRecursively(decl.initializer);
182-
}
183-
else {
184-
addNodeWithRecursiveChild(decl, decl.initializer);
185-
}
186-
break;
173+
break;
187174

188-
case SyntaxKind.ArrowFunction:
189-
case SyntaxKind.FunctionDeclaration:
190-
case SyntaxKind.FunctionExpression:
191-
addNodeWithRecursiveChild(node, (<FunctionLikeDeclaration>node).body);
192-
break;
193-
194-
case SyntaxKind.EnumDeclaration:
195-
startNode(node);
196-
for (const member of (<EnumDeclaration>node).members) {
197-
if (!isComputedProperty(member)) {
198-
addLeafNode(member);
175+
case SyntaxKind.BindingElement:
176+
case SyntaxKind.VariableDeclaration:
177+
const decl = <VariableDeclaration>node;
178+
const name = decl.name;
179+
if (isBindingPattern(name)) {
180+
addChildrenRecursively(name);
199181
}
200-
}
201-
endNode();
202-
break;
182+
else if (decl.initializer && isFunctionOrClassExpression(decl.initializer)) {
183+
// For `const x = function() {}`, just use the function node, not the const.
184+
addChildrenRecursively(decl.initializer);
185+
}
186+
else {
187+
addNodeWithRecursiveChild(decl, decl.initializer, addChildrenRecursively);
188+
}
189+
break;
203190

204-
case SyntaxKind.ClassDeclaration:
205-
case SyntaxKind.ClassExpression:
206-
case SyntaxKind.InterfaceDeclaration:
207-
startNode(node);
208-
for (const member of (<InterfaceDeclaration>node).members) {
209-
addChildrenRecursively(member);
210-
}
211-
endNode();
212-
break;
191+
case SyntaxKind.ArrowFunction:
192+
case SyntaxKind.FunctionDeclaration:
193+
case SyntaxKind.FunctionExpression:
194+
addNodeWithRecursiveChild(node, (<FunctionLikeDeclaration>node).body, addChildrenRecursively);
195+
break;
213196

214-
case SyntaxKind.ModuleDeclaration:
215-
addNodeWithRecursiveChild(node, getInteriorModule(<ModuleDeclaration>node).body);
216-
break;
197+
case SyntaxKind.EnumDeclaration:
198+
startNode(node);
199+
for (const member of (<EnumDeclaration>node).members) {
200+
if (!isComputedProperty(member)) {
201+
addLeafNode(member);
202+
}
203+
}
204+
endNode();
205+
break;
217206

218-
case SyntaxKind.ExportSpecifier:
219-
case SyntaxKind.ImportEqualsDeclaration:
220-
case SyntaxKind.IndexSignature:
221-
case SyntaxKind.CallSignature:
222-
case SyntaxKind.ConstructSignature:
223-
case SyntaxKind.TypeAliasDeclaration:
224-
addLeafNode(node);
225-
break;
207+
case SyntaxKind.ClassDeclaration:
208+
case SyntaxKind.ClassExpression:
209+
case SyntaxKind.InterfaceDeclaration:
210+
startNode(node);
211+
for (const member of (<InterfaceDeclaration>node).members) {
212+
addChildrenRecursively(member);
213+
}
214+
endNode();
215+
break;
226216

227-
default:
228-
forEach(node.jsDoc, jsDoc => {
229-
forEach(jsDoc.tags, tag => {
230-
if (tag.kind === SyntaxKind.JSDocTypedefTag) {
231-
addLeafNode(tag);
232-
}
217+
case SyntaxKind.ModuleDeclaration:
218+
addNodeWithRecursiveChild(node, getInteriorModule(<ModuleDeclaration>node).body, addChildrenRecursively);
219+
break;
220+
221+
case SyntaxKind.ExportSpecifier:
222+
case SyntaxKind.ImportEqualsDeclaration:
223+
case SyntaxKind.IndexSignature:
224+
case SyntaxKind.CallSignature:
225+
case SyntaxKind.ConstructSignature:
226+
case SyntaxKind.TypeAliasDeclaration:
227+
addLeafNode(node);
228+
break;
229+
230+
default:
231+
forEach(node.jsDoc, jsDoc => {
232+
forEach(jsDoc.tags, tag => {
233+
if (tag.kind === SyntaxKind.JSDocTypedefTag) {
234+
addLeafNode(tag);
235+
}
236+
});
233237
});
234-
});
235238

236-
forEachChild(node, addChildrenRecursively);
239+
forEachChild(node, addChildrenRecursively);
240+
}
237241
}
242+
243+
addChildrenRecursively(node);
238244
}
239245

240246
/** Merge declarations of the same kind. */

0 commit comments

Comments
 (0)