diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 708de68ec61..3ef2c6ada2a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22,6 +22,17 @@ namespace ts { } export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { + // Cancellation that controls whether or not we can cancel in the middle of type checking. + // In general cancelling is *not* safe for the type checker. We might be in the middle of + // computing something, and we will leave our internals in an inconsistent state. Callers + // who set the cancellation token should catch if a cancellation exception occurs, and + // should throw away and create a new TypeChecker. + // + // Currently we only support setting the cancellation token when getting diagnostics. This + // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if + // they no longer need the information (for example, if the user started editing again). + let cancellationToken: CancellationToken; + let Symbol = objectAllocator.getSymbolConstructor(); let Type = objectAllocator.getTypeConstructor(); let Signature = objectAllocator.getSignatureConstructor(); @@ -194,10 +205,10 @@ namespace ts { return checker; - function getEmitResolver(sourceFile?: SourceFile) { + function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) { // Ensure we have all the type information in place for this file so that all the // emitter questions of this resolver will return the right information. - getDiagnostics(sourceFile); + getDiagnostics(sourceFile, cancellationToken); return emitResolver; } @@ -13028,8 +13039,24 @@ namespace ts { } function checkSourceElement(node: Node): void { - if (!node) return; - switch (node.kind) { + if (!node) { + return; + } + + let kind = node.kind; + if (cancellationToken) { + // Only bother checking on a few construct kinds. We don't want to be excessivly + // hitting the cancellation token on every node we check. + switch (kind) { + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.FunctionDeclaration: + cancellationToken.throwIfCancellationRequested(); + } + } + + switch (kind) { case SyntaxKind.TypeParameter: return checkTypeParameter(node); case SyntaxKind.Parameter: @@ -13305,7 +13332,20 @@ namespace ts { } } - function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] { + function getDiagnostics(sourceFile: SourceFile, ct: CancellationToken): Diagnostic[] { + try { + // Record the cancellation token so it can be checked later on during checkSourceElement. + // Do this in a finally block so we can ensure that it gets reset back to nothing after + // this call is done. + cancellationToken = ct; + return getDiagnosticsWorker(sourceFile); + } + finally { + cancellationToken = undefined; + } + } + + function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] { throwIfNonDiagnosticsProducing(); if (sourceFile) { checkSourceFile(sourceFile); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 823349eda91..427084b5779 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -805,4 +805,4 @@ namespace ts { Debug.assert(false, message); } } -} +} diff --git a/src/compiler/program.ts b/src/compiler/program.ts index b9321ab3461..28efaf24cca 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -104,14 +104,14 @@ namespace ts { }; } - export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile): Diagnostic[] { - let diagnostics = program.getOptionsDiagnostics().concat( - program.getSyntacticDiagnostics(sourceFile), - program.getGlobalDiagnostics(), - program.getSemanticDiagnostics(sourceFile)); + export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[] { + let diagnostics = program.getOptionsDiagnostics(cancellationToken).concat( + program.getSyntacticDiagnostics(sourceFile, cancellationToken), + program.getGlobalDiagnostics(cancellationToken), + program.getSemanticDiagnostics(sourceFile, cancellationToken)); if (program.getCompilerOptions().declaration) { - diagnostics.concat(program.getDeclarationDiagnostics(sourceFile)); + diagnostics.concat(program.getDeclarationDiagnostics(sourceFile, cancellationToken)); } return sortAndDeduplicateDiagnostics(diagnostics); @@ -233,10 +233,15 @@ namespace ts { return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false)); } - function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback): EmitResult { + function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult { + return runWithCancellationToken(() => emitWorker(this, sourceFile, writeFileCallback, cancellationToken)); + } + + function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken): EmitResult { // If the noEmitOnError flag is set, then check if we have any errors so far. If so, - // immediately bail out. - if (options.noEmitOnError && getPreEmitDiagnostics(this).length > 0) { + // immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we + // get any preEmit diagnostics, not just the ones + if (options.noEmitOnError && getPreEmitDiagnostics(program, /*sourceFile:*/ undefined, cancellationToken).length > 0) { return { diagnostics: [], sourceMaps: undefined, emitSkipped: true }; } @@ -265,53 +270,86 @@ namespace ts { return filesByName.get(fileName); } - function getDiagnosticsHelper(sourceFile: SourceFile, getDiagnostics: (sourceFile: SourceFile) => Diagnostic[]): Diagnostic[] { + function getDiagnosticsHelper( + sourceFile: SourceFile, + getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => Diagnostic[], + cancellationToken: CancellationToken): Diagnostic[] { if (sourceFile) { - return getDiagnostics(sourceFile); + return getDiagnostics(sourceFile, cancellationToken); } let allDiagnostics: Diagnostic[] = []; forEach(program.getSourceFiles(), sourceFile => { - addRange(allDiagnostics, getDiagnostics(sourceFile)); + if (cancellationToken) { + cancellationToken.throwIfCancellationRequested(); + } + addRange(allDiagnostics, getDiagnostics(sourceFile, cancellationToken)); }); return sortAndDeduplicateDiagnostics(allDiagnostics); } - function getSyntacticDiagnostics(sourceFile?: SourceFile): Diagnostic[] { - return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile); + function getSyntacticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { + return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken); } - function getSemanticDiagnostics(sourceFile?: SourceFile): Diagnostic[] { - return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile); + function getSemanticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { + return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken); } - function getDeclarationDiagnostics(sourceFile?: SourceFile): Diagnostic[] { - return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile); + function getDeclarationDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { + return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken); } - function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] { + function getSyntacticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { return sourceFile.parseDiagnostics; } - function getSemanticDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] { - let typeChecker = getDiagnosticsProducingTypeChecker(); + 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 agressive 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; + } - Debug.assert(!!sourceFile.bindDiagnostics); - let bindDiagnostics = sourceFile.bindDiagnostics; - let checkDiagnostics = typeChecker.getDiagnostics(sourceFile); - let programDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName); - - return bindDiagnostics.concat(checkDiagnostics).concat(programDiagnostics); + throw e; + } } - function getDeclarationDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] { - if (!isDeclarationFile(sourceFile)) { - let resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile); - // Don't actually write any files since we're just getting diagnostics. - var writeFile: WriteFileCallback = () => { }; - return ts.getDeclarationDiagnostics(getEmitHost(writeFile), resolver, sourceFile); - } + function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { + return runWithCancellationToken(() => { + let typeChecker = getDiagnosticsProducingTypeChecker(); + + Debug.assert(!!sourceFile.bindDiagnostics); + let bindDiagnostics = sourceFile.bindDiagnostics; + let checkDiagnostics = typeChecker.getDiagnostics(sourceFile, cancellationToken); + let programDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName); + + return bindDiagnostics.concat(checkDiagnostics).concat(programDiagnostics); + }); + } + + function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] { + return runWithCancellationToken(() => { + if (!isDeclarationFile(sourceFile)) { + let resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken); + // Don't actually write any files since we're just getting diagnostics. + var writeFile: WriteFileCallback = () => { }; + return ts.getDeclarationDiagnostics(getEmitHost(writeFile), resolver, sourceFile); + } + }); } function getOptionsDiagnostics(): Diagnostic[] { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4117ad09f75..6c2f72a1ab7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1290,6 +1290,15 @@ namespace ts { (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void): void; } + export class OperationCanceledException { } + + export interface CancellationToken { + isCancellationRequested(): boolean; + + /** @throws OperationCanceledException if isCancellationRequested is true */ + throwIfCancellationRequested(): void; + } + export interface Program extends ScriptReferenceHost { /** * Get a list of files in the program @@ -1306,13 +1315,13 @@ namespace ts { * used for writing the JavaScript and declaration files. Otherwise, the writeFile parameter * will be invoked when writing the JavaScript and declaration files. */ - emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback): EmitResult; + emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult; - getOptionsDiagnostics(): Diagnostic[]; - getGlobalDiagnostics(): Diagnostic[]; - getSyntacticDiagnostics(sourceFile?: SourceFile): Diagnostic[]; - getSemanticDiagnostics(sourceFile?: SourceFile): Diagnostic[]; - getDeclarationDiagnostics(sourceFile?: SourceFile): Diagnostic[]; + getOptionsDiagnostics(cancellationToken?: CancellationToken): Diagnostic[]; + getGlobalDiagnostics(cancellationToken?: CancellationToken): Diagnostic[]; + getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; + getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; /** * Gets a type checker that can be used to semantically analyze source fils in the program. @@ -1423,9 +1432,9 @@ namespace ts { getJsxIntrinsicTagNames(): Symbol[]; // Should not be called directly. Should only be accessed through the Program instance. - /* @internal */ getDiagnostics(sourceFile?: SourceFile): Diagnostic[]; + /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; /* @internal */ getGlobalDiagnostics(): Diagnostic[]; - /* @internal */ getEmitResolver(sourceFile?: SourceFile): EmitResolver; + /* @internal */ getEmitResolver(sourceFile?: SourceFile, cancellationToken?: CancellationToken): EmitResolver; /* @internal */ getNodeCount(): number; /* @internal */ getIdentifierCount(): number; @@ -2178,14 +2187,9 @@ namespace ts { verticalTab = 0x0B, // \v } - export interface CancellationToken { - isCancellationRequested(): boolean; - } - export interface CompilerHost { getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; getDefaultLibFileName(options: CompilerOptions): string; - getCancellationToken? (): CancellationToken; writeFile: WriteFileCallback; getCurrentDirectory(): string; getCanonicalFileName(fileName: string): string; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 0717ed68e83..208237b8b64 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -190,14 +190,14 @@ module FourSlash { return "\nMarker: " + currentTestState.lastKnownMarker + "\nChecking: " + msg + "\n\n"; } - export class TestCancellationToken implements ts.CancellationToken { + export class TestCancellationToken implements ts.HostCancellationToken { // 0 - cancelled // >0 - not cancelled // <0 - not cancelled and value denotes number of isCancellationRequested after which token become cancelled - private static NotCancelled: number = -1; - private numberOfCallsBeforeCancellation: number = TestCancellationToken.NotCancelled; - public isCancellationRequested(): boolean { + private static NotCanceled: number = -1; + private numberOfCallsBeforeCancellation: number = TestCancellationToken.NotCanceled; + public isCancellationRequested(): boolean { if (this.numberOfCallsBeforeCancellation < 0) { return false; } @@ -216,7 +216,7 @@ module FourSlash { } public resetCancelled(): void { - this.numberOfCallsBeforeCancellation = TestCancellationToken.NotCancelled; + this.numberOfCallsBeforeCancellation = TestCancellationToken.NotCanceled; } } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 8eb77817533..6cb92df5948 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -103,14 +103,11 @@ module Harness.LanguageService { } } - class CancellationToken { - public static None: CancellationToken = new CancellationToken(null); - - constructor(private cancellationToken: ts.CancellationToken) { - } + class DefaultHostCancellationToken implements ts.HostCancellationToken { + public static Instance = new DefaultHostCancellationToken(); public isCancellationRequested() { - return this.cancellationToken && this.cancellationToken.isCancellationRequested(); + return false; } } @@ -124,8 +121,8 @@ module Harness.LanguageService { export class LanguageServiceAdapterHost { protected fileNameToScript: ts.Map = {}; - constructor(protected cancellationToken: ts.CancellationToken = CancellationToken.None, - protected settings = ts.getDefaultCompilerOptions()) { + constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, + protected settings = ts.getDefaultCompilerOptions()) { } public getNewLine(): string { @@ -173,8 +170,8 @@ module Harness.LanguageService { /// Native adapter class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost { - getCompilationSettings(): ts.CompilerOptions { return this.settings; } - getCancellationToken(): ts.CancellationToken { return this.cancellationToken; } + getCompilationSettings() { return this.settings; } + getCancellationToken() { return this.cancellationToken; } getCurrentDirectory(): string { return ""; } getDefaultLibFileName(): string { return ""; } getScriptFileNames(): string[] { return this.getFilenames(); } @@ -194,7 +191,7 @@ module Harness.LanguageService { export class NativeLanugageServiceAdapter implements LanguageServiceAdapter { private host: NativeLanguageServiceHost; - constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) { + constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { this.host = new NativeLanguageServiceHost(cancellationToken, options); } getHost() { return this.host; } @@ -206,7 +203,7 @@ module Harness.LanguageService { /// Shim adapter class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost, ts.CoreServicesShimHost { private nativeHost: NativeLanguageServiceHost; - constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) { + constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { super(cancellationToken, options); this.nativeHost = new NativeLanguageServiceHost(cancellationToken, options); } @@ -218,7 +215,7 @@ module Harness.LanguageService { positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToLineAndCharacter(fileName, position); } getCompilationSettings(): string { return JSON.stringify(this.nativeHost.getCompilationSettings()); } - getCancellationToken(): ts.CancellationToken { return this.nativeHost.getCancellationToken(); } + getCancellationToken(): ts.HostCancellationToken { return this.nativeHost.getCancellationToken(); } getCurrentDirectory(): string { return this.nativeHost.getCurrentDirectory(); } getDefaultLibFileName(): string { return this.nativeHost.getDefaultLibFileName(); } getScriptFileNames(): string { return JSON.stringify(this.nativeHost.getScriptFileNames()); } @@ -399,7 +396,7 @@ module Harness.LanguageService { export class ShimLanugageServiceAdapter implements LanguageServiceAdapter { private host: ShimLanguageServiceHost; private factory: ts.TypeScriptServicesFactory; - constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) { + constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { this.host = new ShimLanguageServiceHost(cancellationToken, options); this.factory = new TypeScript.Services.TypeScriptServicesFactory(); } @@ -446,7 +443,7 @@ module Harness.LanguageService { class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost { private client: ts.server.SessionClient; - constructor(cancellationToken: ts.CancellationToken, settings: ts.CompilerOptions) { + constructor(cancellationToken: ts.HostCancellationToken, settings: ts.CompilerOptions) { super(cancellationToken, settings); } @@ -575,7 +572,7 @@ module Harness.LanguageService { export class ServerLanugageServiceAdapter implements LanguageServiceAdapter { private host: SessionClientHost; private client: ts.server.SessionClient; - constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) { + constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { // This is the main host that tests use to direct tests var clientHost = new SessionClientHost(cancellationToken, options); var client = new ts.server.SessionClient(clientHost); diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index a4cc7ec2ad5..bc506bc22f9 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -2,7 +2,7 @@ namespace ts.NavigateTo { type RawNavigateToItem = { name: string; fileName: string; matchKind: PatternMatchKind; isCaseSensitive: boolean; declaration: Declaration }; - export function getNavigateToItems(program: Program, cancellationToken: CancellationTokenObject, searchValue: string, maxResultCount: number): NavigateToItem[] { + export function getNavigateToItems(program: Program, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number): NavigateToItem[] { let patternMatcher = createPatternMatcher(searchValue); let rawItems: RawNavigateToItem[] = []; diff --git a/src/services/services.ts b/src/services/services.ts index 1e98e29d4cf..9996a2ac5fa 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -944,6 +944,10 @@ namespace ts { } } + export interface HostCancellationToken { + isCancellationRequested(): boolean; + } + // // Public interface of the host of a language service instance. // @@ -955,7 +959,7 @@ namespace ts { getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot; getLocalizedDiagnosticMessages?(): any; - getCancellationToken?(): CancellationToken; + getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; getDefaultLibFileName(options: CompilerOptions): string; log? (s: string): void; @@ -1615,25 +1619,6 @@ namespace ts { }; } - export class OperationCanceledException { } - - export class CancellationTokenObject { - public static None: CancellationTokenObject = new CancellationTokenObject(null) - - constructor(private cancellationToken: CancellationToken) { - } - - public isCancellationRequested() { - return this.cancellationToken && this.cancellationToken.isCancellationRequested(); - } - - public throwIfCancellationRequested(): void { - if (this.isCancellationRequested()) { - throw new OperationCanceledException(); - } - } - } - // Cache host information about scrip Should be refreshed // at each language service public entry point, since we don't know when // set of scripts handled by the host changes. @@ -2400,6 +2385,21 @@ namespace ts { return ScriptElementKind.unknown; } + class CancellationTokenObject implements CancellationToken { + constructor(private cancellationToken: HostCancellationToken) { + } + + public isCancellationRequested() { + return this.cancellationToken && this.cancellationToken.isCancellationRequested(); + } + + public throwIfCancellationRequested(): void { + if (this.isCancellationRequested()) { + throw new OperationCanceledException(); + } + } + } + export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry = createDocumentRegistry()): LanguageService { let syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host); let ruleProvider: formatting.RulesProvider; @@ -2604,7 +2604,7 @@ namespace ts { function getSyntacticDiagnostics(fileName: string) { synchronizeHostData(); - return program.getSyntacticDiagnostics(getValidSourceFile(fileName)); + return program.getSyntacticDiagnostics(getValidSourceFile(fileName), cancellationToken); } /** @@ -2626,13 +2626,13 @@ namespace ts { // Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file. // Therefore only get diagnostics for given file. - let semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile); + let semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile, cancellationToken); if (!program.getCompilerOptions().declaration) { return semanticDiagnostics; } // If '-d' is enabled, check for emitter error. One example of emitter error is export class implements non-export interface - let declarationDiagnostics = program.getDeclarationDiagnostics(targetSourceFile); + let declarationDiagnostics = program.getDeclarationDiagnostics(targetSourceFile, cancellationToken); return concatenate(semanticDiagnostics, declarationDiagnostics); } @@ -2795,7 +2795,8 @@ namespace ts { function getCompilerOptionsDiagnostics() { synchronizeHostData(); - return program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()); + return program.getOptionsDiagnostics(cancellationToken).concat( + program.getGlobalDiagnostics(cancellationToken)); } /// Completion @@ -5815,7 +5816,7 @@ namespace ts { }); } - let emitOutput = program.emit(sourceFile, writeFile); + let emitOutput = program.emit(sourceFile, writeFile, cancellationToken); return { outputFiles, diff --git a/src/services/shims.ts b/src/services/shims.ts index 22c0d3f6065..6e765eff499 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -51,7 +51,7 @@ namespace ts { getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): ScriptSnapshotShim; getLocalizedDiagnosticMessages(): string; - getCancellationToken(): CancellationToken; + getCancellationToken(): HostCancellationToken; getCurrentDirectory(): string; getDefaultLibFileName(options: string): string; getNewLine?(): string; @@ -326,7 +326,7 @@ namespace ts { } } - public getCancellationToken(): CancellationToken { + public getCancellationToken(): HostCancellationToken { var hostCancellationToken = this.shimHost.getCancellationToken(); return new ThrottledCancellationToken(hostCancellationToken); } @@ -348,13 +348,13 @@ namespace ts { } /** A cancellation that throttles calls to the host */ - class ThrottledCancellationToken implements CancellationToken { + class ThrottledCancellationToken implements HostCancellationToken { // Store when we last tried to cancel. Checking cancellation can be expensive (as we have // to marshall over to the host layer). So we only bother actually checking once enough // time has passed. private lastCancellationCheckTime = 0; - constructor(private hostCancellationToken: CancellationToken) { + constructor(private hostCancellationToken: HostCancellationToken) { } public isCancellationRequested(): boolean { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index fce4e2c3025..44b022a7b12 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -178,7 +178,7 @@ namespace ts.SignatureHelp { argumentCount: number; } - export function getSignatureHelpItems(program: Program, sourceFile: SourceFile, position: number, cancellationToken: CancellationTokenObject): SignatureHelpItems { + export function getSignatureHelpItems(program: Program, sourceFile: SourceFile, position: number, cancellationToken: CancellationToken): SignatureHelpItems { let typeChecker = program.getTypeChecker(); // Decide whether to show signature help