diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 083c812464d..b94efe18222 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -887,18 +887,28 @@ namespace ts { return sourceFile.parseDiagnostics; } - function onCancel() { - // Because our type checker might be a bad state, we need to throw it away. - // Note: we are overly aggressive here. We do not actually *have* to throw away - // the "noDiagnosticsTypeChecker". However, for simplicity, i'd like to keep - // the lifetimes of these two TypeCheckers the same. Also, we generally only - // cancel when the user has made a change anyways. And, in that case, we (the - // program instance) will get thrown away anyways. So trying to keep one of - // these type checkers alive doesn't serve much purpose. - noDiagnosticsTypeChecker = undefined; - diagnosticsProducingTypeChecker = undefined; - } + function runWithCancellationToken(func: () => T): T { + try { + return func(); + } + catch (e) { + if (e instanceof OperationCanceledException) { + // We were canceled while performing the operation. Because our type checker + // might be a bad state, we need to throw it away. + // + // Note: we are overly aggressive here. We do not actually *have* to throw away + // the "noDiagnosticsTypeChecker". However, for simplicity, i'd like to keep + // the lifetimes of these two TypeCheckers the same. Also, we generally only + // cancel when the user has made a change anyways. And, in that case, we (the + // program instance) will get thrown away anyways. So trying to keep one of + // these type checkers alive doesn't serve much purpose. + noDiagnosticsTypeChecker = undefined; + diagnosticsProducingTypeChecker = undefined; + } + throw e; + } + } function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { return runWithCancellationToken(() => { @@ -914,7 +924,7 @@ namespace ts { const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName); return bindDiagnostics.concat(checkDiagnostics, fileProcessingDiagnosticsInFile, programDiagnosticsInFile); - }, onCancel); + }); } function getJavaScriptSyntacticDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] { @@ -1093,7 +1103,7 @@ namespace ts { function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic { return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2); } - }, onCancel); + }); } function getDeclarationDiagnosticsWorker(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { @@ -1101,7 +1111,7 @@ namespace ts { const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken); // Don't actually write any files since we're just getting diagnostics. return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile); - }, onCancel); + }); } function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 2910a3528d0..62ead677d8b 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -17,21 +17,27 @@ namespace ts.NavigationBar { export function getNavigationBarItems(sourceFile: SourceFile, cancellationToken: ThrottledCancellationToken): NavigationBarItem[] { curCancellationToken = cancellationToken; curSourceFile = sourceFile; - const result = runWithCancellationToken(() => map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem), () => curSourceFile = undefined); - curSourceFile = undefined; - return result; + try { + return map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem); + } + finally { + curSourceFile = undefined; + } } export function getNavigationTree(sourceFile: SourceFile, cancellationToken: ThrottledCancellationToken): NavigationTree { curCancellationToken = cancellationToken; curSourceFile = sourceFile; - const result = runWithCancellationToken(() => convertToTree(rootNavigationBarNode(sourceFile)), () => curSourceFile = undefined); - curSourceFile = undefined; - return result; + try { + return convertToTree(rootNavigationBarNode(sourceFile)); + } + finally { + curSourceFile = undefined; + } } // Keep sourceFile handy so we don't have to search for it every time we need to call `getText`. - let curCancellationToken: ThrottledCancellationToken; + let curCancellationToken: CancellationToken; let curSourceFile: SourceFile; function nodeText(node: Node): string { return node.getText(curSourceFile); @@ -63,7 +69,7 @@ namespace ts.NavigationBar { const root: NavigationBarNode = { node: sourceFile, additionalNodes: undefined, parent: undefined, children: undefined, indent: 0 }; parent = root; for (const statement of sourceFile.statements) { - addChildrenRecursively(statement, curCancellationToken); + addChildrenRecursively(statement); } endNode(); Debug.assert(!parent && !parentsStack.length); @@ -113,9 +119,9 @@ namespace ts.NavigationBar { } /** Look for navigation bar items in node's subtree, adding them to the current `parent`. */ - function addChildrenRecursively(node: Node, cancellationToken: ThrottledCancellationToken): void { + function addChildrenRecursively(node: Node): void { function addChildrenRecursively(node: Node): void { - cancellationToken.throwIfCancellationRequested(); + curCancellationToken.throwIfCancellationRequested(); if (!node || isToken(node)) { return;