diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b2b55085afe..4ea24b834c5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -739,10 +739,10 @@ module ts { }; } - function typeToString(type: Type, flags?: TypeFormatFlags): string { + function typeToString(type: Type, enclosingDeclaration?:Node, flags?: TypeFormatFlags): string { var stringWriter = createSingleLineTextWriter(); // TODO(shkamat): typeToString should take enclosingDeclaration as input, once we have implemented enclosingDeclaration - writeTypeToTextWriter(type, /*enclosingDeclaration*/ null, flags, stringWriter); + writeTypeToTextWriter(type, enclosingDeclaration, flags, stringWriter); return stringWriter.getText(); } @@ -1379,7 +1379,7 @@ module ts { type.baseTypes.push(baseType); } else { - error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, TypeFormatFlags.WriteArrayAsGenericType)); + error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); } } else { @@ -1420,7 +1420,7 @@ module ts { type.baseTypes.push(baseType); } else { - error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, TypeFormatFlags.WriteArrayAsGenericType)); + error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); } } else { @@ -1987,7 +1987,7 @@ module ts { type = createTypeReference(type, map(node.typeArguments, t => getTypeFromTypeNode(t))); } else { - error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length); + error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length); type = undefined; } } @@ -6194,7 +6194,12 @@ module ts { function getSymbolOfIdentifier(identifier: Identifier) { if (isExpression(identifier)) { if (isRightSideOfQualifiedName()) { - // TODO + var node = identifier.parent; + var symbol = getNodeLinks(node).resolvedSymbol; + if (!symbol) { + checkPropertyAccess(node); + } + return getNodeLinks(node).resolvedSymbol; } return resolveEntityName(identifier, identifier, SymbolFlags.Value); } @@ -6425,7 +6430,11 @@ module ts { getReturnTypeOfSignature: getReturnTypeOfSignature, resolveEntityName: resolveEntityName, getSymbolsInScope: getSymbolsInScope, - getSymbolOfIdentifier: getSymbolOfIdentifier + getSymbolOfIdentifier: getSymbolOfIdentifier, + getTypeOfExpression: checkExpression, + typeToString: typeToString, + symbolToString: symbolToString, + writeTypeToTextWriter: writeTypeToTextWriter }; return checker; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 825e2147586..9078e8a4b56 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -599,6 +599,10 @@ module ts { resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolOfIdentifier(identifier: Identifier): Symbol; + getTypeOfExpression(node: Expression, contextualType?: Type, contextualMapper?: TypeMapper): Type; + typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; + symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; + writeTypeToTextWriter(type: Type, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void; } export interface TextWriter { diff --git a/src/services/pullLanguageService.ts b/src/services/pullLanguageService.ts index 41ae8e94cf4..9286c4ea203 100644 --- a/src/services/pullLanguageService.ts +++ b/src/services/pullLanguageService.ts @@ -564,8 +564,122 @@ module TypeScript.Services { getCompletionEntryDetails(filename: string, position: number, entryName: string) { return undefined; } - getTypeAtPosition(filename: string, position: number) { - return undefined; + + private getNodeAtPosition(sourceFile: ts.SourceFile, position: number) { + var current: ts.Node = sourceFile; + outer: while (true) { + // find the child that has this + for (var i = 0, n = current.getChildCount(); i < n; i++) { + var child = current.getChildAt(i); + if (ts.getTokenPosOfNode(child) <= position && position < child.end) { + current = child; + continue outer; + } + if (child.end > position) break; + } + return current; + } + } + + getEnclosingDeclaration(node: ts.Node): ts.Node { + while (true) { + node = node.parent; + if (!node) { + return node; + } + switch (node.kind) { + case ts.SyntaxKind.Method: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ModuleDeclaration: + return node; + } + } + } + + + getSymbolKind(symbol: ts.Symbol): string { + var flags = symbol.getFlags(); + if (flags & ts.SymbolFlags.Module) return ScriptElementKind.moduleElement; + if (flags & ts.SymbolFlags.Class) return ScriptElementKind.classElement; + if (flags & ts.SymbolFlags.Interface) return ScriptElementKind.interfaceElement; + if (flags & ts.SymbolFlags.Enum) return ScriptElementKind.enumElement; + if (flags & ts.SymbolFlags.Variable) return ScriptElementKind.variableElement; + if (flags & ts.SymbolFlags.Function) return ScriptElementKind.functionElement; + if (flags & ts.SymbolFlags.GetAccessor) return ScriptElementKind.memberGetAccessorElement; + if (flags & ts.SymbolFlags.SetAccessor) return ScriptElementKind.memberSetAccessorElement; + if (flags & ts.SymbolFlags.Method) return ScriptElementKind.memberFunctionElement; + if (flags & ts.SymbolFlags.Property) return ScriptElementKind.memberVariableElement; + if (flags & ts.SymbolFlags.IndexSignature) return ScriptElementKind.indexSignatureElement; + if (flags & ts.SymbolFlags.ConstructSignature) return ScriptElementKind.constructSignatureElement; + if (flags & ts.SymbolFlags.CallSignature) return ScriptElementKind.callSignatureElement; + if (flags & ts.SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement; + if (flags & ts.SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; + if (flags & ts.SymbolFlags.EnumMember) return ScriptElementKind.variableElement; + return ScriptElementKind.unknown; + } + + getTypeKind(type: ts.Type): string { + var flags = type.getFlags(); + if (flags & ts.TypeFlags.Enum) return ScriptElementKind.enumElement; + if (flags & ts.TypeFlags.Class) return ScriptElementKind.classElement; + if (flags & ts.TypeFlags.Interface) return ScriptElementKind.interfaceElement; + if (flags & ts.TypeFlags.TypeParameter) return ScriptElementKind.typeParameterElement; + if (flags & ts.TypeFlags.Intrinsic) return ScriptElementKind.primitiveType; + if (flags & ts.TypeFlags.StringLiteral) return ScriptElementKind.primitiveType; + return ScriptElementKind.unknown; + } + + getTypeAtPosition(filename: string, position: number): TypeInfo { + this.synchronizeHostData(); + + filename = TypeScript.switchToForwardSlashes(filename); + var document = this.documentsByName[filename]; + var node = this.getNodeAtPosition(document.sourceFile(), position); + if (!node) return undefined; + + switch (node.kind) { + // A declaration + case ts.SyntaxKind.Identifier: + if (node.parent.kind === ts.SyntaxKind.CallExpression || node.parent.kind === ts.SyntaxKind.NewExpression) { + // TODO: handle new and call expressions + } + + var symbol = this.typeChecker. getSymbolOfIdentifier(node); + Debug.assert(symbol, "getTypeAtPosition: Could not find symbol for node"); + var type = this.typeChecker.getTypeOfSymbol(symbol); + + return { + memberName: new MemberNameString(this.typeChecker.typeToString(type)), + docComment: "", + fullSymbolName: this.typeChecker.symbolToString(symbol, this.getEnclosingDeclaration(node)), + kind: this.getSymbolKind(symbol), + minChar: node.pos, + limChar: node.end + }; + + // An Expression + case ts.SyntaxKind.ThisKeyword: + case ts.SyntaxKind.QualifiedName: + case ts.SyntaxKind.SuperKeyword: + case ts.SyntaxKind.StringLiteral: + var type = this.typeChecker.getTypeOfExpression(node); + Debug.assert(type, "getTypeAtPosition: Could not find type for node"); + return { + memberName: new MemberNameString(""), + docComment: "", + fullSymbolName: this.typeChecker.typeToString(type, this.getEnclosingDeclaration(node)), + kind: this.getTypeKind(type), + minChar: node.pos, + limChar: node.end + }; + break; + } } getSignatureAtPosition(filename: string, position: number) { return undefined; @@ -589,7 +703,6 @@ module TypeScript.Services { return undefined; } - private getTypeInfoEligiblePath(filename: string, position: number, isConstructorValidPosition: boolean) { var sourceUnit = this._syntaxTreeCache.getCurrentFileSyntaxTree(filename).sourceUnit(); diff --git a/src/services/services.ts b/src/services/services.ts index 69685d79fc0..5f9d3a2f708 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -41,88 +41,6 @@ module ts { getReturnType(): Type; } - interface HostFileInformation { - version: string; - isOpen: boolean; - byteOrderMark: ByteOrderMark; - sourceText?: IScriptSnapshot; - } - - // - // Public services of a language service instance associated - // with a language service host instance - // - export interface LanguageService { - getSyntacticDiagnostics(filename: string): Diagnostic[]; - getSemanticDiagnostics(filename: string): Diagnostic[]; - } - - // - // Public interface of the host of a language service instance. - // - export interface LanguageServiceHost { - log(s: string): void; - - getCompilationSettings(): CompilerOptions; - - getScriptFileNames(): string[]; - getScriptVersion(filename: string): string; - getScriptIsOpen(filename: string): boolean; - getScriptByteOrderMark(filename: string): ByteOrderMark; - getScriptSnapshot(filename: string): IScriptSnapshot; - getLocalizedDiagnosticMessages(): any; - //getCancellationToken(): CancellationToken; - } - - // Represents an immutable snapshot of a script at a specified time. Once acquired, the - // snapshot is observably immutable. i.e. the same calls with the same parameters will return - // the same values. - export interface IScriptSnapshot { - // Get's a portion of the script snapshot specified by [start, end). - getText(start: number, end: number): string; - - // Get's the length of this script snapshot. - getLength(): number; - - // This call returns the array containing the start position of every line. - // i.e."[0, 10, 55]". TODO: consider making this optional. The language service could - // always determine this (albeit in a more expensive manner). - getLineStartPositions(): number[]; - - // Gets the TextChangeRange that describe how the text changed between this text and - // an older version. This informatoin is used by the incremental parser to determine - // what sections of the script need to be reparsed. 'null' can be returned if the - // change range cannot be determined. However, in that case, incremental parsing will - // not happen and the entire document will be reparsed. - getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange; - } - - export interface Span { - start(): number; - end(): number; - } - - export interface TextChange { - span: Span; - newText: string; - } - - export interface TextChangeRange { - span(): Span; - newLength(): number; - } - - export enum ByteOrderMark { - None = 0, - Utf8 = 1, - Utf16BigEndian = 2, - Utf16LittleEndian = 3, - } - - export interface CancellationToken { - isCancellationRequested(): boolean; - } - var scanner: Scanner = createScanner(ScriptTarget.ES5); var emptyArray: any [] = []; @@ -335,86 +253,6 @@ module ts { } } - export function createLanguageService(host: LanguageServiceHost): LanguageService { - - var program: Program; - var typeChecker: TypeChecker; - var filesByName: Map; - - function createCompilerHost(): CompilerHost { - return { - getSourceFile: (filename, languageVersion) => { - var hostFile = filesByName[filename]; - - // TODO use the document registry to get or update - - if (!hostFile.sourceText) { - hostFile.sourceText = host.getScriptSnapshot(filename); - } - - // TODO add support for IScriptSnapshot in the parser - return createSourceFile(filename, hostFile.sourceText.getText(0, hostFile.sourceText.getLength()), languageVersion); - }, - // Need something that doesn't depend on sys.ts here - getDefaultLibFilename: () => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), "lib.d.ts"), - getCancellationToken: (): CancellationToken => undefined, - writeFile: (fileName, data) => { - throw Error("TODO: write file"); - }, - getCurrentDirectory: (): string => { - throw Error("TODO: getCurrentDirectory"); - }, - getCanonicalFileName: getCanonicalFileName, - useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames - }; - } - - function synchronizeHostData(): void { - // Build the cache - filesByName = {}; - var files = host.getScriptFileNames(); - forEach(files, (f) => { - filesByName[f] = { - version: host.getScriptVersion(f), - isOpen: host.getScriptIsOpen(f), - byteOrderMark: host.getScriptByteOrderMark(f) - }; - }); - - var currentProgram = program; - - var options = host.getCompilationSettings(); - - // update the program - program = createProgram(files, options, createCompilerHost()); - - // Update the typeChecker - typeChecker = program.getTypeChecker(); - - // TODO release old sources from the registry - if (currentProgram) { - - } - } - - function getSyntacticDiagnostics(filename: string): Diagnostic[] { - synchronizeHostData(); - var sourceFile = program.getSourceFile(filename); - return sourceFile ? program.getDiagnostics(sourceFile) : []; - } - - function getSemanticDiagnostics(filename: string): Diagnostic[] { - synchronizeHostData(); - var sourceFile = program.getSourceFile(filename); - return sourceFile ? typeChecker.getDiagnostics(sourceFile) : []; - } - - return { - getSyntacticDiagnostics: getSyntacticDiagnostics, - getSemanticDiagnostics: getSemanticDiagnostics, - }; - } - function initializeServices() { objectAllocator = { getNodeConstructor: kind => { diff --git a/src/services/typescriptServices.ts b/src/services/typescriptServices.ts index 908b9bf063a..9c79c2db5b0 100644 --- a/src/services/typescriptServices.ts +++ b/src/services/typescriptServices.ts @@ -13,6 +13,7 @@ // limitations under the License. // +/// /// ///