Skip to content

Commit e009d68

Browse files
Merge pull request microsoft#3556 from Microsoft/cancellableClassification
Make classification cancellable.
2 parents 71c6433 + 370372e commit e009d68

5 files changed

+87
-41
lines changed

Diff for: src/services/services.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -1618,7 +1618,6 @@ namespace ts {
16181618
export class OperationCanceledException { }
16191619

16201620
export class CancellationTokenObject {
1621-
16221621
public static None: CancellationTokenObject = new CancellationTokenObject(null)
16231622

16241623
constructor(private cancellationToken: CancellationToken) {
@@ -6064,6 +6063,26 @@ namespace ts {
60646063
return convertClassifications(getEncodedSemanticClassifications(fileName, span));
60656064
}
60666065

6066+
function checkForClassificationCancellation(kind: SyntaxKind) {
6067+
// We don't want to actually call back into our host on every node to find out if we've
6068+
// been canceled. That would be an enormous amount of chattyness, along with the all
6069+
// the overhead of marshalling the data to/from the host. So instead we pick a few
6070+
// reasonable node kinds to bother checking on. These node kinds represent high level
6071+
// constructs that we would expect to see commonly, but just at a far less frequent
6072+
// interval.
6073+
//
6074+
// For example, in checker.ts (around 750k) we only have around 600 of these constructs.
6075+
// That means we're calling back into the host around every 1.2k of the file we process.
6076+
// Lib.d.ts has similar numbers.
6077+
switch (kind) {
6078+
case SyntaxKind.ModuleDeclaration:
6079+
case SyntaxKind.ClassDeclaration:
6080+
case SyntaxKind.InterfaceDeclaration:
6081+
case SyntaxKind.FunctionDeclaration:
6082+
cancellationToken.throwIfCancellationRequested();
6083+
}
6084+
}
6085+
60676086
function getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications {
60686087
synchronizeHostData();
60696088

@@ -6131,7 +6150,10 @@ namespace ts {
61316150
function processNode(node: Node) {
61326151
// Only walk into nodes that intersect the requested span.
61336152
if (node && textSpanIntersectsWith(span, node.getFullStart(), node.getFullWidth())) {
6134-
if (node.kind === SyntaxKind.Identifier && !nodeIsMissing(node)) {
6153+
let kind = node.kind;
6154+
checkForClassificationCancellation(kind);
6155+
6156+
if (kind === SyntaxKind.Identifier && !nodeIsMissing(node)) {
61356157
let identifier = <Identifier>node;
61366158

61376159
// Only bother calling into the typechecker if this is an identifier that
@@ -6498,6 +6520,8 @@ namespace ts {
64986520

64996521
// Ignore nodes that don't intersect the original span to classify.
65006522
if (decodedTextSpanIntersectsWith(spanStart, spanLength, element.pos, element.getFullWidth())) {
6523+
checkForClassificationCancellation(element.kind);
6524+
65016525
let children = element.getChildren(sourceFile);
65026526
for (let i = 0, n = children.length; i < n; i++) {
65036527
let child = children[i];

Diff for: src/services/shims.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ namespace ts {
327327
}
328328

329329
public getCancellationToken(): CancellationToken {
330-
return this.shimHost.getCancellationToken();
330+
var hostCancellationToken = this.shimHost.getCancellationToken();
331+
return new ThrottledCancellationToken(hostCancellationToken);
331332
}
332333

333334
public getCurrentDirectory(): string {
@@ -346,6 +347,29 @@ namespace ts {
346347
}
347348
}
348349

350+
/** A cancellation that throttles calls to the host */
351+
class ThrottledCancellationToken implements CancellationToken {
352+
// Store when we last tried to cancel. Checking cancellation can be expensive (as we have
353+
// to marshall over to the host layer). So we only bother actually checking once enough
354+
// time has passed.
355+
private lastCancellationCheckTime = 0;
356+
357+
constructor(private hostCancellationToken: CancellationToken) {
358+
}
359+
360+
public isCancellationRequested(): boolean {
361+
var time = Date.now();
362+
var duration = Math.abs(time - this.lastCancellationCheckTime);
363+
if (duration > 10) {
364+
// Check no more than once every 10 ms.
365+
this.lastCancellationCheckTime = time;
366+
return this.hostCancellationToken.isCancellationRequested();
367+
}
368+
369+
return false;
370+
}
371+
}
372+
349373
export class CoreServicesShimHostAdapter implements ParseConfigHost {
350374

351375
constructor(private shimHost: CoreServicesShimHost) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////module M {
4+
////}
5+
////module N {
6+
////}
7+
8+
var c = classification;
9+
cancellation.setCancelled(1);
10+
verifyOperationIsCancelled(() => verify.semanticClassificationsAre());
11+
cancellation.resetCancelled();
12+
13+
verify.semanticClassificationsAre(
14+
c.moduleName("M"),
15+
c.moduleName("N"));

Diff for: tests/cases/fourslash/shims/cancellationWhenfindingAllRefsOnDefinition.ts

-38
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////module M {
4+
////}
5+
////module N {
6+
////}
7+
8+
var c = classification;
9+
cancellation.setCancelled(1);
10+
verifyOperationIsCancelled(() => verify.syntacticClassificationsAre());
11+
cancellation.resetCancelled();
12+
13+
verify.syntacticClassificationsAre(
14+
c.keyword("module"),
15+
c.moduleName("M"),
16+
c.punctuation("{"),
17+
c.punctuation("}"),
18+
c.keyword("module"),
19+
c.moduleName("N"),
20+
c.punctuation("{"),
21+
c.punctuation("}"));

0 commit comments

Comments
 (0)