Merge branch 'master' into noImportClauseBuilders

Conflicts:
	src/services/services.ts
This commit is contained in:
Daniel Rosenwasser
2015-07-06 16:43:54 -07:00
24 changed files with 305 additions and 158 deletions

View File

@@ -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[] = [];

View File

@@ -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,26 +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.
@@ -2401,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;
@@ -2605,7 +2604,7 @@ namespace ts {
function getSyntacticDiagnostics(fileName: string) {
synchronizeHostData();
return program.getSyntacticDiagnostics(getValidSourceFile(fileName));
return program.getSyntacticDiagnostics(getValidSourceFile(fileName), cancellationToken);
}
/**
@@ -2627,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);
}
@@ -2796,7 +2795,8 @@ namespace ts {
function getCompilerOptionsDiagnostics() {
synchronizeHostData();
return program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
return program.getOptionsDiagnostics(cancellationToken).concat(
program.getGlobalDiagnostics(cancellationToken));
}
/// Completion
@@ -3205,21 +3205,30 @@ namespace ts {
}
function tryGetObjectLikeCompletionSymbols(objectLikeContainer: ObjectLiteralExpression | BindingPattern): boolean {
// Object literal expression, look up possible property names from contextual type
// We're looking up possible property names from contextual/inferred/declared type.
isMemberCompletion = true;
isNewIdentifierLocation = true;
let typeForObject: Type;
let existingMembers: Declaration[];
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
// We are completing on contextual types, but may also include properties
// other than those within the declared type.
isNewIdentifierLocation = true;
typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
}
else {
else if (objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern) {
// We are *only* completing on properties from the type being destructured.
isNewIdentifierLocation = false;
typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
existingMembers = (<BindingPattern>objectLikeContainer).elements;
}
else {
Debug.fail("Expected object literal or binding pattern, got " + objectLikeContainer.kind);
}
if (!typeForObject) {
return false;
@@ -5834,7 +5843,7 @@ namespace ts {
});
}
let emitOutput = program.emit(sourceFile, writeFile);
let emitOutput = program.emit(sourceFile, writeFile, cancellationToken);
return {
outputFiles,
@@ -6082,6 +6091,26 @@ namespace ts {
return convertClassifications(getEncodedSemanticClassifications(fileName, span));
}
function checkForClassificationCancellation(kind: SyntaxKind) {
// We don't want to actually call back into our host on every node to find out if we've
// been canceled. That would be an enormous amount of chattyness, along with the all
// the overhead of marshalling the data to/from the host. So instead we pick a few
// reasonable node kinds to bother checking on. These node kinds represent high level
// constructs that we would expect to see commonly, but just at a far less frequent
// interval.
//
// For example, in checker.ts (around 750k) we only have around 600 of these constructs.
// That means we're calling back into the host around every 1.2k of the file we process.
// Lib.d.ts has similar numbers.
switch (kind) {
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.FunctionDeclaration:
cancellationToken.throwIfCancellationRequested();
}
}
function getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications {
synchronizeHostData();
@@ -6149,7 +6178,10 @@ namespace ts {
function processNode(node: Node) {
// Only walk into nodes that intersect the requested span.
if (node && textSpanIntersectsWith(span, node.getFullStart(), node.getFullWidth())) {
if (node.kind === SyntaxKind.Identifier && !nodeIsMissing(node)) {
let kind = node.kind;
checkForClassificationCancellation(kind);
if (kind === SyntaxKind.Identifier && !nodeIsMissing(node)) {
let identifier = <Identifier>node;
// Only bother calling into the typechecker if this is an identifier that
@@ -6516,6 +6548,8 @@ namespace ts {
// Ignore nodes that don't intersect the original span to classify.
if (decodedTextSpanIntersectsWith(spanStart, spanLength, element.pos, element.getFullWidth())) {
checkForClassificationCancellation(element.kind);
let children = element.getChildren(sourceFile);
for (let i = 0, n = children.length; i < n; i++) {
let child = children[i];

View File

@@ -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,8 +326,9 @@ namespace ts {
}
}
public getCancellationToken(): CancellationToken {
return this.shimHost.getCancellationToken();
public getCancellationToken(): HostCancellationToken {
var hostCancellationToken = this.shimHost.getCancellationToken();
return new ThrottledCancellationToken(hostCancellationToken);
}
public getCurrentDirectory(): string {
@@ -346,6 +347,29 @@ namespace ts {
}
}
/** A cancellation that throttles calls to the host */
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: HostCancellationToken) {
}
public isCancellationRequested(): boolean {
var time = Date.now();
var duration = Math.abs(time - this.lastCancellationCheckTime);
if (duration > 10) {
// Check no more than once every 10 ms.
this.lastCancellationCheckTime = time;
return this.hostCancellationToken.isCancellationRequested();
}
return false;
}
}
export class CoreServicesShimHostAdapter implements ParseConfigHost {
constructor(private shimHost: CoreServicesShimHost) {

View File

@@ -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