From f97bb60d6ad0ad2a835634460ac01d916f1874b6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 13:07:06 -0700 Subject: [PATCH 01/18] Initial work on classified quick info. --- src/compiler/checker.ts | 177 ++++++++++++++++++++++++++++++++------- src/compiler/types.ts | 38 +++++++++ src/services/services.ts | 33 ++++++++ 3 files changed, 219 insertions(+), 29 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b227939dafb..efcce7e6cdd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23,6 +23,20 @@ module ts { return undefined; } + interface SymbolWriter { + write(text: string, kind: SymbolDisplayPartKind, symbol: Symbol): void; + displayPartKind(symbol: Symbol): SymbolDisplayPartKind; + clear(): void; + } + + interface DisplayPartsSymbolWriter extends SymbolWriter { + displayParts(): SymbolDisplayPart[]; + } + + interface StringSymbolWriter extends SymbolWriter { + string(): string; + } + /// fullTypeCheck denotes if this instance of the typechecker will be used to get semantic diagnostics. /// If fullTypeCheck === true, then the typechecker should do every possible check to produce all errors /// If fullTypeCheck === false, the typechecker can take shortcuts and skip checks that only produce errors. @@ -62,7 +76,9 @@ module ts { getTypeOfNode: getTypeOfNode, getApparentType: getApparentType, typeToString: typeToString, + typeToDisplayParts: undefined, symbolToString: symbolToString, + symbolToDisplayParts: symbolToDisplayParts, getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType, getRootSymbol: getRootSymbol, getContextualType: getContextualType, @@ -895,51 +911,154 @@ module ts { { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: firstIdentifierName }; } + var displayPartWriters: DisplayPartsSymbolWriter[] = []; + var stringWriters: StringSymbolWriter[] = []; + + function displayPartKind(symbol: Symbol): SymbolDisplayPartKind { + var flags = symbol.flags; + + if (flags & SymbolFlags.Variable) { + return symbol.declarations && symbol.declarations.length > 0 && symbol.declarations[0].kind === SyntaxKind.Parameter + ? SymbolDisplayPartKind.ParameterName + : SymbolDisplayPartKind.LocalName; + } + else if (flags & SymbolFlags.Property) { return SymbolDisplayPartKind.PropertyName; } + else if (flags & SymbolFlags.EnumMember) { return SymbolDisplayPartKind.EnumMemberName; } + else if (flags & SymbolFlags.Function) { return SymbolDisplayPartKind.FunctionName; } + else if (flags & SymbolFlags.Class) { return SymbolDisplayPartKind.ClassName; } + else if (flags & SymbolFlags.Interface) { return SymbolDisplayPartKind.InterfaceName; } + else if (flags & SymbolFlags.Enum) { return SymbolDisplayPartKind.EnumName; } + else if (flags & SymbolFlags.Module) { return SymbolDisplayPartKind.ModuleName; } + else if (flags & SymbolFlags.Method) { return SymbolDisplayPartKind.MethodName; } + else if (flags & SymbolFlags.TypeParameter) { return SymbolDisplayPartKind.TypeParameterName; } + + return SymbolDisplayPartKind.Text; + } + + function getDisplayPartWriter(): DisplayPartsSymbolWriter { + if (displayPartWriters.length == 0) { + var displayParts: SymbolDisplayPart[] = []; + return { + displayParts: () => displayParts, + write: (text, kind, symbol) => displayParts.push({ text: text, kind: kind, symbol: symbol }), + clear: () => displayParts = [], + displayPartKind: displayPartKind + }; + } + + return displayPartWriters.pop(); + } + + function getStringWriter(): StringSymbolWriter { + if (stringWriters.length == 0) { + var str = ""; + + return { + string: () => str, + write: text => str += text, + clear: () => str = "", + displayPartKind: (symbol: Symbol): SymbolDisplayPartKind => undefined + }; + } + + return stringWriters.pop(); + } + + function releaseDisplayPartWriter(writer: DisplayPartsSymbolWriter) { + writer.clear(); + displayPartWriters.push(writer); + } + + function releaseStringWriter(writer: StringSymbolWriter) { + writer.clear() + stringWriters.push(writer); + } + + function symbolToDisplayParts(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[] { + var writer = getDisplayPartWriter(); + writeSymbol(symbol, enclosingDeclaration, meaning, writer); + + var result = writer.displayParts(); + releaseDisplayPartWriter(writer); + + return result; + } + + function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string { + var writer = getStringWriter(); + writeSymbol(symbol, enclosingDeclaration, meaning, writer); + + var result = writer.string(); + releaseStringWriter(writer); + + return result; + } + // Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope // Meaning needs to be specified if the enclosing declaration is given - function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { - function getSymbolName(symbol: Symbol) { + function writeSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, writer: SymbolWriter): void { + function writeSymbolName(symbol: Symbol): void { if (symbol.declarations && symbol.declarations.length > 0) { var declaration = symbol.declarations[0]; if (declaration.name) { - return identifierToString(declaration.name); + writer.write(identifierToString(declaration.name), writer.displayPartKind(symbol), symbol); + return; + } + } + + writer.write(symbol.name, writer.displayPartKind(symbol), symbol); + } + + var needsDot = false; + function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void { + if (symbol) { + var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning); + + if (!accessibleSymbolChain || + needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { + + // Go up and add our parent. + walkSymbol( + getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol), + getQualifiedLeftMeaning(meaning)); + } + + if (accessibleSymbolChain) { + for (var i = 0, n = accessibleSymbolChain.length; i < n; i++) { + if (needsDot) { + writer.write(".", SymbolDisplayPartKind.Punctuation, /*symbol:*/ undefined); + } + + writeSymbolName(accessibleSymbolChain[i]); + needsDot = true; + } + } + else { + // If we didn't find accessible symbol chain for this symbol, break if this is external module + if (!needsDot && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) { + return; + } + + if (needsDot) { + writer.write(".", SymbolDisplayPartKind.Punctuation, /*symbol:*/ undefined); + } + + writeSymbolName(symbol); + needsDot = true; } } - return symbol.name; } // Get qualified name if (enclosingDeclaration && // TypeParameters do not need qualification !(symbol.flags & SymbolFlags.TypeParameter)) { - var symbolName: string; - while (symbol) { - var isFirstName = !symbolName; - var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning); - var currentSymbolName: string; - if (accessibleSymbolChain) { - currentSymbolName = ts.map(accessibleSymbolChain, accessibleSymbol => getSymbolName(accessibleSymbol)).join("."); - } - else { - // If we didn't find accessible symbol chain for this symbol, break if this is external module - if (!isFirstName && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) { - break; - } - currentSymbolName = getSymbolName(symbol); - } - symbolName = currentSymbolName + (isFirstName ? "" : ("." + symbolName)); - if (accessibleSymbolChain && !needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { - break; - } - symbol = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol); - meaning = getQualifiedLeftMeaning(meaning); - } - - return symbolName; + walkSymbol(symbol, meaning); + return; } - return getSymbolName(symbol); + return writeSymbolName(symbol); } function writeSymbolToTextWriter(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, writer: TextWriter) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e1116150e34..488367c6ada 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -638,6 +638,8 @@ module ts { getApparentType(type: Type): ApparentType; typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; + typeToDisplayParts(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[]; + symbolToDisplayParts(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[]; getFullyQualifiedName(symbol: Symbol): string; getAugmentedPropertiesOfApparentType(type: Type): Symbol[]; getRootSymbol(symbol: Symbol): Symbol; @@ -1169,6 +1171,42 @@ module ts { verticalTab = 0x0B, // \v } + export class SymbolDisplayPart { + constructor(public text: string, + public kind: SymbolDisplayPartKind, + public symbol: Symbol) { + } + } + + export enum SymbolDisplayPartKind { + AliasName, + ClassName, + EnumName, + EventName, + FieldName, + InterfaceName, + Keyword, + LabelName, + LineBreak, + NumericLiteral, + StringLiteral, + LocalName, + MethodName, + ModuleName, + NamespaceName, + Operator, + ParameterName, + PropertyName, + Punctuation, + Space, + AnonymousTypeIndicator, + Text, + TypeParameterName, + EnumMemberName, + FunctionName, + RegularExpressionLiteral, + } + export interface CancellationToken { isCancellationRequested(): boolean; } diff --git a/src/services/services.ts b/src/services/services.ts index 04e63f6dbe1..82a19c6e22f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -495,6 +495,7 @@ module ts { getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails; getTypeAtPosition(fileName: string, position: number): TypeInfo; + getQuickInfo(fileName: string, position: number): SymbolDisplayPart[]; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TypeScript.TextSpan; @@ -652,6 +653,14 @@ module ts { text: string; } + export class QuickInfo { + constructor(public kind: string, + public kindModifiers: string, + public textSpan: TypeScript.TextSpan, + public displayParts: SymbolDisplayPart[]) { + } + } + export class TypeInfo { constructor( public memberName: TypeScript.MemberName, @@ -2212,6 +2221,29 @@ module ts { } /// QuickInfo + function getQuickInfo(fileName: string, position: number): SymbolDisplayPart[] { + synchronizeHostData(); + + fileName = TypeScript.switchToForwardSlashes(fileName); + var sourceFile = getSourceFile(fileName); + var node = getNodeAtPosition(sourceFile, position); + if (!node) { + return undefined; + } + + var symbol = typeInfoResolver.getSymbolInfo(node); + return symbol ? typeInfoResolver.symbolToDisplayParts(symbol) : []; + //var type = symbol && typeInfoResolver.getTypeOfSymbol(symbol); + //if (type) { + // return new TypeInfo( + // new TypeScript.MemberNameString(typeInfoResolver.typeToString(type)), + // "", typeInfoResolver.symbolToString(symbol, getContainerNode(node)), + // getSymbolKind(symbol), TypeScript.TextSpan.fromBounds(node.pos, node.end)); + //} + + //return undefined; + } + function getTypeAtPosition(fileName: string, position: number): TypeInfo { synchronizeHostData(); @@ -4026,6 +4058,7 @@ module ts { getCompletionsAtPosition: getCompletionsAtPosition, getCompletionEntryDetails: getCompletionEntryDetails, getTypeAtPosition: getTypeAtPosition, + getQuickInfo: getQuickInfo, getSignatureHelpItems: (filename, position): SignatureHelpItems => null, getSignatureHelpCurrentArgumentState: (fileName, position, applicableSpanStart): SignatureHelpState => null, getDefinitionAtPosition: getDefinitionAtPosition, From d70e003cd746262e536219fb1f75e9017c63c1b7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 13:13:57 -0700 Subject: [PATCH 02/18] Clean up symbol writing API. --- src/compiler/checker.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index efcce7e6cdd..4fde9d5995a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24,8 +24,8 @@ module ts { } interface SymbolWriter { - write(text: string, kind: SymbolDisplayPartKind, symbol: Symbol): void; - displayPartKind(symbol: Symbol): SymbolDisplayPartKind; + writeKind(text: string, kind: SymbolDisplayPartKind): void; + writeSymbol(text: string, symbol: Symbol): void; clear(): void; } @@ -911,6 +911,7 @@ module ts { { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: firstIdentifierName }; } + // Pool writers to avoid needing to allocate them for every symbol we write. var displayPartWriters: DisplayPartsSymbolWriter[] = []; var stringWriters: StringSymbolWriter[] = []; @@ -940,9 +941,9 @@ module ts { var displayParts: SymbolDisplayPart[] = []; return { displayParts: () => displayParts, - write: (text, kind, symbol) => displayParts.push({ text: text, kind: kind, symbol: symbol }), + writeKind: (text, kind) => displayParts.push({ text: text, kind: kind, symbol: undefined }), + writeSymbol: (text, symbol) => displayParts.push({ text: text, kind: displayPartKind(symbol), symbol: symbol }), clear: () => displayParts = [], - displayPartKind: displayPartKind }; } @@ -955,9 +956,9 @@ module ts { return { string: () => str, - write: text => str += text, + writeKind: (text, kind) => str += text, + writeSymbol: (text, symbol) => str += text, clear: () => str = "", - displayPartKind: (symbol: Symbol): SymbolDisplayPartKind => undefined }; } @@ -1001,12 +1002,12 @@ module ts { if (symbol.declarations && symbol.declarations.length > 0) { var declaration = symbol.declarations[0]; if (declaration.name) { - writer.write(identifierToString(declaration.name), writer.displayPartKind(symbol), symbol); + writer.writeSymbol(identifierToString(declaration.name), symbol); return; } } - writer.write(symbol.name, writer.displayPartKind(symbol), symbol); + writer.writeSymbol(symbol.name, symbol); } var needsDot = false; @@ -1026,7 +1027,7 @@ module ts { if (accessibleSymbolChain) { for (var i = 0, n = accessibleSymbolChain.length; i < n; i++) { if (needsDot) { - writer.write(".", SymbolDisplayPartKind.Punctuation, /*symbol:*/ undefined); + writer.writeKind(".", SymbolDisplayPartKind.Punctuation); } writeSymbolName(accessibleSymbolChain[i]); @@ -1040,7 +1041,7 @@ module ts { } if (needsDot) { - writer.write(".", SymbolDisplayPartKind.Punctuation, /*symbol:*/ undefined); + writer.writeKind(".", SymbolDisplayPartKind.Punctuation); } writeSymbolName(symbol); From 1cb5e2813ec5f69653cb383a5e77049a1726fe86 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 13:14:40 -0700 Subject: [PATCH 03/18] Place symbolToString above symbolToDisplayParts. --- src/compiler/checker.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4fde9d5995a..7dbb70f9cec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -975,16 +975,6 @@ module ts { stringWriters.push(writer); } - function symbolToDisplayParts(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[] { - var writer = getDisplayPartWriter(); - writeSymbol(symbol, enclosingDeclaration, meaning, writer); - - var result = writer.displayParts(); - releaseDisplayPartWriter(writer); - - return result; - } - function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string { var writer = getStringWriter(); writeSymbol(symbol, enclosingDeclaration, meaning, writer); @@ -995,6 +985,16 @@ module ts { return result; } + function symbolToDisplayParts(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[] { + var writer = getDisplayPartWriter(); + writeSymbol(symbol, enclosingDeclaration, meaning, writer); + + var result = writer.displayParts(); + releaseDisplayPartWriter(writer); + + return result; + } + // Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope // Meaning needs to be specified if the enclosing declaration is given function writeSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, writer: SymbolWriter): void { From 778799669e3bf6f3b0e4bedbb49e4f88a542962f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 15:06:27 -0700 Subject: [PATCH 04/18] Support getting symbolDisplayParts for types as well as symbols. --- src/compiler/checker.ts | 250 +++++++++++++++++++++++++++------------- src/compiler/emitter.ts | 15 ++- src/compiler/types.ts | 3 +- 3 files changed, 175 insertions(+), 93 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7dbb70f9cec..1ac694b0d9f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26,7 +26,15 @@ module ts { interface SymbolWriter { writeKind(text: string, kind: SymbolDisplayPartKind): void; writeSymbol(text: string, symbol: Symbol): void; + writeLine(): void; + increaseIndent(): void; + decreaseIndent(): void; clear(): void; + + // Called when the symbol writer encounters a symbol to write. Currently only used by the + // declaration emitter to help determine if it should patch up the final declaration file + // with import statements it previously saw (but chose not to emit). + trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; } interface DisplayPartsSymbolWriter extends SymbolWriter { @@ -943,7 +951,14 @@ module ts { displayParts: () => displayParts, writeKind: (text, kind) => displayParts.push({ text: text, kind: kind, symbol: undefined }), writeSymbol: (text, symbol) => displayParts.push({ text: text, kind: displayPartKind(symbol), symbol: symbol }), + + // Completely ignore indentation for display part writers. And map newlines to + // a single space. + writeLine: () => displayParts.push({ text: " ", kind: SymbolDisplayPartKind.Space, symbol: undefined }), + increaseIndent: () => { }, + decreaseIndent: () => { }, clear: () => displayParts = [], + trackSymbol: () => { } }; } @@ -958,7 +973,14 @@ module ts { string: () => str, writeKind: (text, kind) => str += text, writeSymbol: (text, symbol) => str += text, + + // Completely ignore indentation for string writers. And map newlines to + // a single space. + writeLine: () => str += " ", + increaseIndent: () => { }, + decreaseIndent: () => { }, clear: () => str = "", + trackSymbol: () => { } }; } @@ -977,7 +999,7 @@ module ts { function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string { var writer = getStringWriter(); - writeSymbol(symbol, enclosingDeclaration, meaning, writer); + writeSymbol(symbol, writer, enclosingDeclaration, meaning); var result = writer.string(); releaseStringWriter(writer); @@ -987,7 +1009,7 @@ module ts { function symbolToDisplayParts(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[] { var writer = getDisplayPartWriter(); - writeSymbol(symbol, enclosingDeclaration, meaning, writer); + writeSymbol(symbol, writer, enclosingDeclaration, meaning); var result = writer.displayParts(); releaseDisplayPartWriter(writer); @@ -997,7 +1019,7 @@ module ts { // Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope // Meaning needs to be specified if the enclosing declaration is given - function writeSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, writer: SymbolWriter): void { + function writeSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags): void { function writeSymbolName(symbol: Symbol): void { if (symbol.declarations && symbol.declarations.length > 0) { var declaration = symbol.declarations[0]; @@ -1010,6 +1032,15 @@ module ts { writer.writeSymbol(symbol.name, symbol); } + // Let the writer know we just wrote out a symbol. The declarationemitter writer uses + // this to determine if an import it has previously seen (and not writter out) needs + // to be written to the file once the walk of the tree is complete. + // + // NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree + // up front (for example, during checking) could determien if we need to emit the imports + // and we could then access that data during declaration emit. + writer.trackSymbol(symbol, enclosingDeclaration, meaning); + var needsDot = false; function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void { if (symbol) { @@ -1066,55 +1097,51 @@ module ts { writer.write(symbolToString(symbol, enclosingDeclaration, meaning)); } - function createSingleLineTextWriter(maxLength?: number) { - var result = ""; - var overflow = false; - function write(s: string) { - if (!overflow) { - result += s; - if (result.length > maxLength) { - result = result.substr(0, maxLength - 3) + "..."; - overflow = true; - } - } - } - return { - write: write, - writeSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { - writeSymbolToTextWriter(symbol, enclosingDeclaration, meaning, this); - }, - writeLine() { - write(" "); - }, - increaseIndent() { }, - decreaseIndent() { }, - getText() { - return result; - } - }; - } - function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { + var writer = getStringWriter(); + writeType(type, enclosingDeclaration, flags, writer); + + var result = writer.string(); + releaseStringWriter(writer); + var maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100; - var stringWriter = createSingleLineTextWriter(maxLength); - // TODO(shkamat): typeToString should take enclosingDeclaration as input, once we have implemented enclosingDeclaration - writeTypeToTextWriter(type, enclosingDeclaration, flags, stringWriter); - return stringWriter.getText(); + if (maxLength && result.length >= maxLength) { + result = result.substr(0, maxLength - "...".length) + "..."; + } + + return result; } - function writeTypeToTextWriter(type: Type, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter) { + function typeToDisplayParts(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] { + var writer = getDisplayPartWriter(); + writeType(type, enclosingDeclaration, flags, writer); + + var result = writer.displayParts(); + releaseDisplayPartWriter(writer); + + return result; + } + + function writeType(type: Type, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void { + //var maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100; + //var stringWriter = createSingleLineTextWriter(maxLength); + // TODO(shkamat): typeToString should take enclosingDeclaration as input, once we have implemented enclosingDeclaration + writeTypeToWriter(type, writer, enclosingDeclaration, flags); + } + + function writeTypeToWriter(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) { var typeStack: Type[]; return writeType(type, /*allowFunctionOrConstructorTypeLiteral*/ true); function writeType(type: Type, allowFunctionOrConstructorTypeLiteral: boolean) { if (type.flags & TypeFlags.Intrinsic) { - writer.write((type).intrinsicName); + writer.writeKind((type).intrinsicName, SymbolDisplayPartKind.Keyword); } else if (type.flags & TypeFlags.Reference) { writeTypeReference(type); } else if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) { - writer.writeSymbol(type.symbol, enclosingDeclaration, SymbolFlags.Type); + writeSymbol(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type); } else if (type.flags & TypeFlags.Tuple) { writeTupleType(type); @@ -1123,18 +1150,24 @@ module ts { writeAnonymousType(type, allowFunctionOrConstructorTypeLiteral); } else if (type.flags & TypeFlags.StringLiteral) { - writer.write((type).text); + writer.writeKind((type).text, SymbolDisplayPartKind.StringLiteral); } else { // Should never get here - writer.write("{ ... }"); + // { ... } + writer.writeKind("{", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("...", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("}", SymbolDisplayPartKind.Punctuation); } } function writeTypeList(types: Type[]) { for (var i = 0; i < types.length; i++) { if (i > 0) { - writer.write(", "); + writer.writeKind(",", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); } writeType(types[i], /*allowFunctionOrConstructorTypeLiteral*/ true); } @@ -1145,20 +1178,20 @@ module ts { // If we are writing array element type the arrow style signatures are not allowed as // we need to surround it by curlies, e.g. { (): T; }[]; as () => T[] would mean something different writeType(type.typeArguments[0], /*allowFunctionOrConstructorTypeLiteral*/ false); - writer.write("[]"); + writer.writeKind("[]", SymbolDisplayPartKind.Punctuation); } else { - writer.writeSymbol(type.target.symbol, enclosingDeclaration, SymbolFlags.Type); - writer.write("<"); + writeSymbol(type.target.symbol, writer, enclosingDeclaration, SymbolFlags.Type); + writer.writeKind("<", SymbolDisplayPartKind.Punctuation); writeTypeList(type.typeArguments); - writer.write(">"); + writer.writeKind(">", SymbolDisplayPartKind.Punctuation); } } function writeTupleType(type: TupleType) { - writer.write("["); + writer.writeKind("[", SymbolDisplayPartKind.Punctuation); writeTypeList(type.elementTypes); - writer.write("]"); + writer.writeKind("]", SymbolDisplayPartKind.Punctuation); } function writeAnonymousType(type: ObjectType, allowFunctionOrConstructorTypeLiteral: boolean) { @@ -1172,7 +1205,7 @@ module ts { } else if (typeStack && contains(typeStack, type)) { // Recursive usage, use any - writer.write("any"); + writer.writeKind("any", SymbolDisplayPartKind.Keyword); } else { if (!typeStack) { @@ -1202,15 +1235,16 @@ module ts { } function writeTypeofSymbol(type: ObjectType) { - writer.write("typeof "); - writer.writeSymbol(type.symbol, enclosingDeclaration, SymbolFlags.Value); + writer.writeKind("typeof", SymbolDisplayPartKind.Keyword); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writeSymbol(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value); } function writeLiteralType(type: ObjectType, allowFunctionOrConstructorTypeLiteral: boolean) { var resolved = resolveObjectTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexType && !resolved.numberIndexType) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { - writer.write("{}"); + writer.writeKind("{}", SymbolDisplayPartKind.Punctuation); return; } @@ -1220,37 +1254,54 @@ module ts { return; } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { - writer.write("new "); + writer.writeKind("new", SymbolDisplayPartKind.Keyword); + writer.writeKind(" ", SymbolDisplayPartKind.Space); writeSignature(resolved.constructSignatures[0], /*arrowStyle*/ true); return; } } } - writer.write("{"); + writer.writeKind("{", SymbolDisplayPartKind.Punctuation); writer.writeLine(); writer.increaseIndent(); for (var i = 0; i < resolved.callSignatures.length; i++) { writeSignature(resolved.callSignatures[i]); - writer.write(";"); + writer.writeKind(";", SymbolDisplayPartKind.Punctuation); writer.writeLine(); } for (var i = 0; i < resolved.constructSignatures.length; i++) { - writer.write("new "); + writer.writeKind("new", SymbolDisplayPartKind.Keyword); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writeSignature(resolved.constructSignatures[i]); - writer.write(";"); + writer.writeKind(";", SymbolDisplayPartKind.Punctuation); writer.writeLine(); } if (resolved.stringIndexType) { - writer.write("[x: string]: "); + // [x: string]: + writer.writeKind("[", SymbolDisplayPartKind.Punctuation); + writer.writeKind("x", SymbolDisplayPartKind.ParameterName); + writer.writeKind(":", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("string", SymbolDisplayPartKind.Keyword); + writer.writeKind("]:", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); writeType(resolved.stringIndexType, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.write(";"); + writer.writeKind(";", SymbolDisplayPartKind.Punctuation); writer.writeLine(); } if (resolved.numberIndexType) { - writer.write("[x: number]: "); + // [x: number]: + writer.writeKind("[", SymbolDisplayPartKind.Punctuation); + writer.writeKind("x", SymbolDisplayPartKind.ParameterName); + writer.writeKind(":", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("number", SymbolDisplayPartKind.Keyword); + writer.writeKind("]:", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); writeType(resolved.numberIndexType, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.write(";"); + writer.writeKind(";", SymbolDisplayPartKind.Punctuation); writer.writeLine(); } for (var i = 0; i < resolved.properties.length; i++) { @@ -1259,64 +1310,81 @@ module ts { if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfType(t).length) { var signatures = getSignaturesOfType(t, SignatureKind.Call); for (var j = 0; j < signatures.length; j++) { - writer.writeSymbol(p); + writeSymbol(p, writer); if (isOptionalProperty(p)) { - writer.write("?"); + writer.writeKind("?", SymbolDisplayPartKind.Punctuation); } writeSignature(signatures[j]); - writer.write(";"); + writer.writeKind(";", SymbolDisplayPartKind.Punctuation); writer.writeLine(); } } else { - writer.writeSymbol(p); + writeSymbol(p, writer); if (isOptionalProperty(p)) { - writer.write("?"); + writer.writeKind("?", SymbolDisplayPartKind.Punctuation); } - writer.write(": "); + writer.writeKind(":", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); writeType(t, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.write(";"); + writer.writeKind(";", SymbolDisplayPartKind.Punctuation); writer.writeLine(); } } writer.decreaseIndent(); - writer.write("}"); + writer.writeKind("}", SymbolDisplayPartKind.Punctuation); } function writeSignature(signature: Signature, arrowStyle?: boolean) { if (signature.typeParameters) { - writer.write("<"); + writer.writeKind("<", SymbolDisplayPartKind.Punctuation); for (var i = 0; i < signature.typeParameters.length; i++) { if (i > 0) { - writer.write(", "); + writer.writeKind(",", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); } var tp = signature.typeParameters[i]; - writer.writeSymbol(tp.symbol); + writeSymbol(tp.symbol, writer); var constraint = getConstraintOfTypeParameter(tp); if (constraint) { - writer.write(" extends "); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("extends", SymbolDisplayPartKind.Keyword); + writer.writeKind(" ", SymbolDisplayPartKind.Space); writeType(constraint, /*allowFunctionOrConstructorTypeLiteral*/ true); } } - writer.write(">"); + writer.writeKind(">", SymbolDisplayPartKind.Punctuation); } - writer.write("("); + writer.writeKind("(", SymbolDisplayPartKind.Punctuation); for (var i = 0; i < signature.parameters.length; i++) { if (i > 0) { - writer.write(", "); + writer.writeKind(",", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); } var p = signature.parameters[i]; if (getDeclarationFlagsFromSymbol(p) & NodeFlags.Rest) { - writer.write("..."); + writer.writeKind("...", SymbolDisplayPartKind.Punctuation); } - writer.writeSymbol(p); + writeSymbol(p, writer); if (p.valueDeclaration.flags & NodeFlags.QuestionMark || (p.valueDeclaration).initializer) { - writer.write("?"); + writer.writeKind("?", SymbolDisplayPartKind.Punctuation); } - writer.write(": "); + writer.writeKind(":", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writeType(getTypeOfSymbol(p), /*allowFunctionOrConstructorTypeLiteral*/ true); } - writer.write(arrowStyle ? ") => " : "): "); + + writer.writeKind(")", SymbolDisplayPartKind.Punctuation); + if (arrowStyle) { + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("=>", SymbolDisplayPartKind.Punctuation); + } + else { + writer.writeKind(":", SymbolDisplayPartKind.Punctuation); + } + writer.writeKind(" ", SymbolDisplayPartKind.Space); + writeType(getReturnTypeOfSignature(signature), /*allowFunctionOrConstructorTypeLiteral*/ true); } } @@ -7411,17 +7479,34 @@ module ts { return getNodeLinks(node).enumMemberValue; } + // Create a single instance that we can wrap the underlying emitter TextWriter with. That + // way we don't have to allocate a new wrapper every time writeTypeAtLocation and + // writeReturnTypeOfSignatureDeclaration are called. + var emitSymbolWriter = { + writer: undefined, + + writeKind: function (text: string) { this.writer.write(text) }, + writeSymbol: function (text: string) { this.writer.write(text) }, + writeLine: function () { this.writer.writeLine() }, + increaseIndent: function () { this.writer.increaseIndent() }, + decreaseIndent: function () { this.writer.decreaseIndent() }, + clear: function () { }, + trackSymbol: function (symbol: Symbol, declaration: Node, meaning: SymbolFlags) { this.writer.trackSymbol(symbol, declaration, meaning) } + }; + function writeTypeAtLocation(location: Node, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter) { // Get type of the symbol if this is the valid symbol otherwise get type at location var symbol = getSymbolOfNode(location); var type = symbol && !(symbol.flags & SymbolFlags.TypeLiteral) ? getTypeOfSymbol(symbol) : getTypeFromTypeNode(location); - writeTypeToTextWriter(type, enclosingDeclaration, flags, writer); + emitSymbolWriter.writer = writer; + writeTypeToWriter(type, emitSymbolWriter, enclosingDeclaration, flags); } function writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter) { var signature = getSignatureFromDeclaration(signatureDeclaration); - writeTypeToTextWriter(getReturnTypeOfSignature(signature), enclosingDeclaration, flags , writer); + emitSymbolWriter.writer = writer; + writeTypeToWriter(getReturnTypeOfSignature(signature), emitSymbolWriter, enclosingDeclaration, flags); } function invokeEmitter(targetSourceFile?: SourceFile) { @@ -7440,7 +7525,6 @@ module ts { isImplementationOfOverload: isImplementationOfOverload, writeTypeAtLocation: writeTypeAtLocation, writeReturnTypeOfSignatureDeclaration: writeReturnTypeOfSignatureDeclaration, - writeSymbol: writeSymbolToTextWriter, isSymbolAccessible: isSymbolAccessible, isImportDeclarationEntityNameReferenceDeclarationVisibile: isImportDeclarationEntityNameReferenceDeclarationVisibile }; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 73064eda695..96dd86ec537 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -101,7 +101,7 @@ module ts { }; } - function createTextWriter(writeSymbol: (symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags)=> void): EmitTextWriter { + function createTextWriter(trackSymbol: (symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags)=> void): EmitTextWriter { var output = ""; var indent = 0; var lineStart = true; @@ -149,7 +149,7 @@ module ts { return { write: write, - writeSymbol: writeSymbol, + trackSymbol: trackSymbol, rawWrite: rawWrite, writeLiteral: writeLiteral, writeLine: writeLine, @@ -307,7 +307,7 @@ module ts { } function emitJavaScript(jsFilePath: string, root?: SourceFile) { - var writer = createTextWriter(writeSymbol); + var writer = createTextWriter(trackSymbol); var write = writer.write; var writeLine = writer.writeLine; var increaseIndent = writer.increaseIndent; @@ -363,7 +363,7 @@ module ts { /** Sourcemap data that will get encoded */ var sourceMapData: SourceMapData; - function writeSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) { } + function trackSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) { } function initializeEmitterWithSourceMaps() { var sourceMapDir: string; // The directory in which sourcemap will be @@ -2252,7 +2252,7 @@ module ts { } function emitDeclarations(jsFilePath: string, root?: SourceFile) { - var writer = createTextWriter(writeSymbol); + var writer = createTextWriter(trackSymbol); var write = writer.write; var writeLine = writer.writeLine; var increaseIndent = writer.increaseIndent; @@ -2280,7 +2280,7 @@ module ts { var oldWriter = writer; forEach(importDeclarations, aliasToWrite => { var aliasEmitInfo = forEach(aliasDeclarationEmitInfo, declEmitInfo => declEmitInfo.declaration === aliasToWrite ? declEmitInfo : undefined); - writer = createTextWriter(writeSymbol); + writer = createTextWriter(trackSymbol); for (var declarationIndent = aliasEmitInfo.indent; declarationIndent; declarationIndent--) { writer.increaseIndent(); } @@ -2291,10 +2291,9 @@ module ts { writer = oldWriter; } - function writeSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { + function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { var symbolAccesibilityResult = resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning); if (symbolAccesibilityResult.accessibility === SymbolAccessibility.Accessible) { - resolver.writeSymbol(symbol, enclosingDeclaration, meaning, writer); // write the aliases if (symbolAccesibilityResult && symbolAccesibilityResult.aliasesToMakeVisible) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 488367c6ada..69c9b917c7a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -648,7 +648,7 @@ module ts { export interface TextWriter { write(s: string): void; - writeSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; + trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; writeLine(): void; increaseIndent(): void; decreaseIndent(): void; @@ -690,7 +690,6 @@ module ts { isImplementationOfOverload(node: FunctionDeclaration): boolean; writeTypeAtLocation(location: Node, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void; writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void; - writeSymbol(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, writer: TextWriter): void; isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult; isImportDeclarationEntityNameReferenceDeclarationVisibile(entityName: EntityName): SymbolAccessiblityResult; } From c3944635dd427284efdf00214780c08da4545d1d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 15:21:55 -0700 Subject: [PATCH 05/18] Flash out QuickInfo implementation. --- src/services/services.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 82a19c6e22f..9aed4def522 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -495,7 +495,7 @@ module ts { getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails; getTypeAtPosition(fileName: string, position: number): TypeInfo; - getQuickInfo(fileName: string, position: number): SymbolDisplayPart[]; + getQuickInfo(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TypeScript.TextSpan; @@ -1722,11 +1722,10 @@ module ts { return undefined; } - var declarations = symbol.getDeclarations(); return { name: displayName, kind: getSymbolKind(symbol), - kindModifiers: declarations ? getNodeModifiers(declarations[0]) : ScriptElementKindModifier.none + kindModifiers: getSymbolModifiers(symbol) }; } @@ -2207,6 +2206,12 @@ module ts { } } + function getSymbolModifiers(symbol: Symbol): string { + return symbol && symbol.declarations && symbol.declarations.length > 0 + ? getNodeModifiers(symbol.declarations[0]) + : ScriptElementKindModifier.none; + } + function getNodeModifiers(node: Node): string { var flags = node.flags; var result: string[] = []; @@ -2221,7 +2226,7 @@ module ts { } /// QuickInfo - function getQuickInfo(fileName: string, position: number): SymbolDisplayPart[] { + function getQuickInfo(fileName: string, position: number): QuickInfo { synchronizeHostData(); fileName = TypeScript.switchToForwardSlashes(fileName); @@ -2232,16 +2237,15 @@ module ts { } var symbol = typeInfoResolver.getSymbolInfo(node); - return symbol ? typeInfoResolver.symbolToDisplayParts(symbol) : []; - //var type = symbol && typeInfoResolver.getTypeOfSymbol(symbol); - //if (type) { - // return new TypeInfo( - // new TypeScript.MemberNameString(typeInfoResolver.typeToString(type)), - // "", typeInfoResolver.symbolToString(symbol, getContainerNode(node)), - // getSymbolKind(symbol), TypeScript.TextSpan.fromBounds(node.pos, node.end)); - //} + if (!symbol) { + return undefined; + } - //return undefined; + return new QuickInfo( + getSymbolKind(symbol), + getSymbolModifiers(symbol), + new TypeScript.TextSpan(node.getStart(), node.getWidth()), + typeInfoResolver.symbolToDisplayParts(symbol)); } function getTypeAtPosition(fileName: string, position: number): TypeInfo { @@ -4038,7 +4042,7 @@ module ts { var kind = getSymbolKind(symbol); if (kind) { return RenameInfo.Create(symbol.name, typeInfoResolver.getFullyQualifiedName(symbol), kind, - getNodeModifiers(symbol.getDeclarations()[0]), + getSymbolModifiers(symbol), new TypeScript.TextSpan(node.getStart(), node.getWidth())); } } From b0c55b2389a52d16e54ab88bb9e13d3e8b20427b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 15:32:27 -0700 Subject: [PATCH 06/18] Hook up quick info shims. --- src/services/services.ts | 20 +++++++++++++++++--- src/services/shims.ts | 12 ++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 9aed4def522..aab25d76212 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -495,7 +495,7 @@ module ts { getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails; getTypeAtPosition(fileName: string, position: number): TypeInfo; - getQuickInfo(fileName: string, position: number): QuickInfo; + getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TypeScript.TextSpan; @@ -659,6 +659,20 @@ module ts { public textSpan: TypeScript.TextSpan, public displayParts: SymbolDisplayPart[]) { } + + public toJSON() { + return { + kind: this.kind, + kindModifiers: this.kindModifiers, + textSpan: this.textSpan, + displayParts: this.displayParts.forEach(d => { + return { + text: d.text, + kind: SymbolDisplayPartKind[d.kind] + }; + }) + }; + } } export class TypeInfo { @@ -2226,7 +2240,7 @@ module ts { } /// QuickInfo - function getQuickInfo(fileName: string, position: number): QuickInfo { + function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo { synchronizeHostData(); fileName = TypeScript.switchToForwardSlashes(fileName); @@ -4062,7 +4076,7 @@ module ts { getCompletionsAtPosition: getCompletionsAtPosition, getCompletionEntryDetails: getCompletionEntryDetails, getTypeAtPosition: getTypeAtPosition, - getQuickInfo: getQuickInfo, + getQuickInfoAtPosition: getQuickInfoAtPosition, getSignatureHelpItems: (filename, position): SignatureHelpItems => null, getSignatureHelpCurrentArgumentState: (fileName, position, applicableSpanStart): SignatureHelpState => null, getDefinitionAtPosition: getDefinitionAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index 210a458b0be..a1767bf0c34 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -87,7 +87,9 @@ module ts { getCompletionsAtPosition(fileName: string, position: number, isMemberCompletion: boolean): string; getCompletionEntryDetails(fileName: string, position: number, entryName: string): string; + getQuickInfoAtPosition(fileName: string, position: number): string; getTypeAtPosition(fileName: string, position: number): string; + getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string; getBreakpointStatementAtPosition(fileName: string, position: number): string; @@ -540,6 +542,16 @@ module ts { /// QUICKINFO /// Computes a string representation of the type at the requested position /// in the active file. + public getQuickInfoAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getQuickInfoAtPosition('" + fileName + "', " + position + ")", + () => { + var quickInfo = this.languageService.getQuickInfoAtPosition(fileName, position); + return quickInfo; + }); + } + + public getTypeAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( "getTypeAtPosition('" + fileName + "', " + position + ")", From 4d1a43a905f9d892b74ed001bcb809483b71164e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 16:02:58 -0700 Subject: [PATCH 07/18] Update names. --- src/compiler/checker.ts | 164 ++++++++++++++++++++-------------------- src/compiler/types.ts | 51 ++++++------- 2 files changed, 107 insertions(+), 108 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1ac694b0d9f..1e7c96b64aa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -928,20 +928,20 @@ module ts { if (flags & SymbolFlags.Variable) { return symbol.declarations && symbol.declarations.length > 0 && symbol.declarations[0].kind === SyntaxKind.Parameter - ? SymbolDisplayPartKind.ParameterName - : SymbolDisplayPartKind.LocalName; + ? SymbolDisplayPartKind.parameterName + : SymbolDisplayPartKind.localName; } - else if (flags & SymbolFlags.Property) { return SymbolDisplayPartKind.PropertyName; } - else if (flags & SymbolFlags.EnumMember) { return SymbolDisplayPartKind.EnumMemberName; } - else if (flags & SymbolFlags.Function) { return SymbolDisplayPartKind.FunctionName; } - else if (flags & SymbolFlags.Class) { return SymbolDisplayPartKind.ClassName; } - else if (flags & SymbolFlags.Interface) { return SymbolDisplayPartKind.InterfaceName; } - else if (flags & SymbolFlags.Enum) { return SymbolDisplayPartKind.EnumName; } - else if (flags & SymbolFlags.Module) { return SymbolDisplayPartKind.ModuleName; } - else if (flags & SymbolFlags.Method) { return SymbolDisplayPartKind.MethodName; } - else if (flags & SymbolFlags.TypeParameter) { return SymbolDisplayPartKind.TypeParameterName; } + else if (flags & SymbolFlags.Property) { return SymbolDisplayPartKind.propertyName; } + else if (flags & SymbolFlags.EnumMember) { return SymbolDisplayPartKind.enumMemberName; } + else if (flags & SymbolFlags.Function) { return SymbolDisplayPartKind.functionName; } + else if (flags & SymbolFlags.Class) { return SymbolDisplayPartKind.className; } + else if (flags & SymbolFlags.Interface) { return SymbolDisplayPartKind.interfaceName; } + else if (flags & SymbolFlags.Enum) { return SymbolDisplayPartKind.enumName; } + else if (flags & SymbolFlags.Module) { return SymbolDisplayPartKind.moduleName; } + else if (flags & SymbolFlags.Method) { return SymbolDisplayPartKind.methodName; } + else if (flags & SymbolFlags.TypeParameter) { return SymbolDisplayPartKind.typeParameterName; } - return SymbolDisplayPartKind.Text; + return SymbolDisplayPartKind.text; } function getDisplayPartWriter(): DisplayPartsSymbolWriter { @@ -954,7 +954,7 @@ module ts { // Completely ignore indentation for display part writers. And map newlines to // a single space. - writeLine: () => displayParts.push({ text: " ", kind: SymbolDisplayPartKind.Space, symbol: undefined }), + writeLine: () => displayParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }), increaseIndent: () => { }, decreaseIndent: () => { }, clear: () => displayParts = [], @@ -1058,7 +1058,7 @@ module ts { if (accessibleSymbolChain) { for (var i = 0, n = accessibleSymbolChain.length; i < n; i++) { if (needsDot) { - writer.writeKind(".", SymbolDisplayPartKind.Punctuation); + writer.writeKind(".", SymbolDisplayPartKind.punctuation); } writeSymbolName(accessibleSymbolChain[i]); @@ -1072,7 +1072,7 @@ module ts { } if (needsDot) { - writer.writeKind(".", SymbolDisplayPartKind.Punctuation); + writer.writeKind(".", SymbolDisplayPartKind.punctuation); } writeSymbolName(symbol); @@ -1135,7 +1135,7 @@ module ts { function writeType(type: Type, allowFunctionOrConstructorTypeLiteral: boolean) { if (type.flags & TypeFlags.Intrinsic) { - writer.writeKind((type).intrinsicName, SymbolDisplayPartKind.Keyword); + writer.writeKind((type).intrinsicName, SymbolDisplayPartKind.keyword); } else if (type.flags & TypeFlags.Reference) { writeTypeReference(type); @@ -1150,24 +1150,24 @@ module ts { writeAnonymousType(type, allowFunctionOrConstructorTypeLiteral); } else if (type.flags & TypeFlags.StringLiteral) { - writer.writeKind((type).text, SymbolDisplayPartKind.StringLiteral); + writer.writeKind((type).text, SymbolDisplayPartKind.stringLiteral); } else { // Should never get here // { ... } - writer.writeKind("{", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); - writer.writeKind("...", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); - writer.writeKind("}", SymbolDisplayPartKind.Punctuation); + writer.writeKind("{", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); + writer.writeKind("...", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); + writer.writeKind("}", SymbolDisplayPartKind.punctuation); } } function writeTypeList(types: Type[]) { for (var i = 0; i < types.length; i++) { if (i > 0) { - writer.writeKind(",", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind(",", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); } writeType(types[i], /*allowFunctionOrConstructorTypeLiteral*/ true); } @@ -1178,20 +1178,20 @@ module ts { // If we are writing array element type the arrow style signatures are not allowed as // we need to surround it by curlies, e.g. { (): T; }[]; as () => T[] would mean something different writeType(type.typeArguments[0], /*allowFunctionOrConstructorTypeLiteral*/ false); - writer.writeKind("[]", SymbolDisplayPartKind.Punctuation); + writer.writeKind("[]", SymbolDisplayPartKind.punctuation); } else { writeSymbol(type.target.symbol, writer, enclosingDeclaration, SymbolFlags.Type); - writer.writeKind("<", SymbolDisplayPartKind.Punctuation); + writer.writeKind("<", SymbolDisplayPartKind.punctuation); writeTypeList(type.typeArguments); - writer.writeKind(">", SymbolDisplayPartKind.Punctuation); + writer.writeKind(">", SymbolDisplayPartKind.punctuation); } } function writeTupleType(type: TupleType) { - writer.writeKind("[", SymbolDisplayPartKind.Punctuation); + writer.writeKind("[", SymbolDisplayPartKind.punctuation); writeTypeList(type.elementTypes); - writer.writeKind("]", SymbolDisplayPartKind.Punctuation); + writer.writeKind("]", SymbolDisplayPartKind.punctuation); } function writeAnonymousType(type: ObjectType, allowFunctionOrConstructorTypeLiteral: boolean) { @@ -1205,7 +1205,7 @@ module ts { } else if (typeStack && contains(typeStack, type)) { // Recursive usage, use any - writer.writeKind("any", SymbolDisplayPartKind.Keyword); + writer.writeKind("any", SymbolDisplayPartKind.keyword); } else { if (!typeStack) { @@ -1235,8 +1235,8 @@ module ts { } function writeTypeofSymbol(type: ObjectType) { - writer.writeKind("typeof", SymbolDisplayPartKind.Keyword); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("typeof", SymbolDisplayPartKind.keyword); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeSymbol(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value); } @@ -1244,7 +1244,7 @@ module ts { var resolved = resolveObjectTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexType && !resolved.numberIndexType) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { - writer.writeKind("{}", SymbolDisplayPartKind.Punctuation); + writer.writeKind("{}", SymbolDisplayPartKind.punctuation); return; } @@ -1254,54 +1254,54 @@ module ts { return; } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { - writer.writeKind("new", SymbolDisplayPartKind.Keyword); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("new", SymbolDisplayPartKind.keyword); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeSignature(resolved.constructSignatures[0], /*arrowStyle*/ true); return; } } } - writer.writeKind("{", SymbolDisplayPartKind.Punctuation); + writer.writeKind("{", SymbolDisplayPartKind.punctuation); writer.writeLine(); writer.increaseIndent(); for (var i = 0; i < resolved.callSignatures.length; i++) { writeSignature(resolved.callSignatures[i]); - writer.writeKind(";", SymbolDisplayPartKind.Punctuation); + writer.writeKind(";", SymbolDisplayPartKind.punctuation); writer.writeLine(); } for (var i = 0; i < resolved.constructSignatures.length; i++) { - writer.writeKind("new", SymbolDisplayPartKind.Keyword); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("new", SymbolDisplayPartKind.keyword); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeSignature(resolved.constructSignatures[i]); - writer.writeKind(";", SymbolDisplayPartKind.Punctuation); + writer.writeKind(";", SymbolDisplayPartKind.punctuation); writer.writeLine(); } if (resolved.stringIndexType) { // [x: string]: - writer.writeKind("[", SymbolDisplayPartKind.Punctuation); - writer.writeKind("x", SymbolDisplayPartKind.ParameterName); - writer.writeKind(":", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); - writer.writeKind("string", SymbolDisplayPartKind.Keyword); - writer.writeKind("]:", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("[", SymbolDisplayPartKind.punctuation); + writer.writeKind("x", SymbolDisplayPartKind.parameterName); + writer.writeKind(":", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); + writer.writeKind("string", SymbolDisplayPartKind.keyword); + writer.writeKind("]:", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeType(resolved.stringIndexType, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.writeKind(";", SymbolDisplayPartKind.Punctuation); + writer.writeKind(";", SymbolDisplayPartKind.punctuation); writer.writeLine(); } if (resolved.numberIndexType) { // [x: number]: - writer.writeKind("[", SymbolDisplayPartKind.Punctuation); - writer.writeKind("x", SymbolDisplayPartKind.ParameterName); - writer.writeKind(":", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); - writer.writeKind("number", SymbolDisplayPartKind.Keyword); - writer.writeKind("]:", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind("[", SymbolDisplayPartKind.punctuation); + writer.writeKind("x", SymbolDisplayPartKind.parameterName); + writer.writeKind(":", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); + writer.writeKind("number", SymbolDisplayPartKind.keyword); + writer.writeKind("]:", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeType(resolved.numberIndexType, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.writeKind(";", SymbolDisplayPartKind.Punctuation); + writer.writeKind(";", SymbolDisplayPartKind.punctuation); writer.writeLine(); } for (var i = 0; i < resolved.properties.length; i++) { @@ -1312,78 +1312,78 @@ module ts { for (var j = 0; j < signatures.length; j++) { writeSymbol(p, writer); if (isOptionalProperty(p)) { - writer.writeKind("?", SymbolDisplayPartKind.Punctuation); + writer.writeKind("?", SymbolDisplayPartKind.punctuation); } writeSignature(signatures[j]); - writer.writeKind(";", SymbolDisplayPartKind.Punctuation); + writer.writeKind(";", SymbolDisplayPartKind.punctuation); writer.writeLine(); } } else { writeSymbol(p, writer); if (isOptionalProperty(p)) { - writer.writeKind("?", SymbolDisplayPartKind.Punctuation); + writer.writeKind("?", SymbolDisplayPartKind.punctuation); } - writer.writeKind(":", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind(":", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeType(t, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.writeKind(";", SymbolDisplayPartKind.Punctuation); + writer.writeKind(";", SymbolDisplayPartKind.punctuation); writer.writeLine(); } } writer.decreaseIndent(); - writer.writeKind("}", SymbolDisplayPartKind.Punctuation); + writer.writeKind("}", SymbolDisplayPartKind.punctuation); } function writeSignature(signature: Signature, arrowStyle?: boolean) { if (signature.typeParameters) { - writer.writeKind("<", SymbolDisplayPartKind.Punctuation); + writer.writeKind("<", SymbolDisplayPartKind.punctuation); for (var i = 0; i < signature.typeParameters.length; i++) { if (i > 0) { - writer.writeKind(",", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind(",", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); } var tp = signature.typeParameters[i]; writeSymbol(tp.symbol, writer); var constraint = getConstraintOfTypeParameter(tp); if (constraint) { - writer.writeKind(" ", SymbolDisplayPartKind.Space); - writer.writeKind("extends", SymbolDisplayPartKind.Keyword); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind(" ", SymbolDisplayPartKind.space); + writer.writeKind("extends", SymbolDisplayPartKind.keyword); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeType(constraint, /*allowFunctionOrConstructorTypeLiteral*/ true); } } - writer.writeKind(">", SymbolDisplayPartKind.Punctuation); + writer.writeKind(">", SymbolDisplayPartKind.punctuation); } - writer.writeKind("(", SymbolDisplayPartKind.Punctuation); + writer.writeKind("(", SymbolDisplayPartKind.punctuation); for (var i = 0; i < signature.parameters.length; i++) { if (i > 0) { - writer.writeKind(",", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind(",", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); } var p = signature.parameters[i]; if (getDeclarationFlagsFromSymbol(p) & NodeFlags.Rest) { - writer.writeKind("...", SymbolDisplayPartKind.Punctuation); + writer.writeKind("...", SymbolDisplayPartKind.punctuation); } writeSymbol(p, writer); if (p.valueDeclaration.flags & NodeFlags.QuestionMark || (p.valueDeclaration).initializer) { - writer.writeKind("?", SymbolDisplayPartKind.Punctuation); + writer.writeKind("?", SymbolDisplayPartKind.punctuation); } - writer.writeKind(":", SymbolDisplayPartKind.Punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind(":", SymbolDisplayPartKind.punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeType(getTypeOfSymbol(p), /*allowFunctionOrConstructorTypeLiteral*/ true); } - writer.writeKind(")", SymbolDisplayPartKind.Punctuation); + writer.writeKind(")", SymbolDisplayPartKind.punctuation); if (arrowStyle) { - writer.writeKind(" ", SymbolDisplayPartKind.Space); - writer.writeKind("=>", SymbolDisplayPartKind.Punctuation); + writer.writeKind(" ", SymbolDisplayPartKind.space); + writer.writeKind("=>", SymbolDisplayPartKind.punctuation); } else { - writer.writeKind(":", SymbolDisplayPartKind.Punctuation); + writer.writeKind(":", SymbolDisplayPartKind.punctuation); } - writer.writeKind(" ", SymbolDisplayPartKind.Space); + writer.writeKind(" ", SymbolDisplayPartKind.space); writeType(getReturnTypeOfSignature(signature), /*allowFunctionOrConstructorTypeLiteral*/ true); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 69c9b917c7a..87c760aecd0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1178,32 +1178,31 @@ module ts { } export enum SymbolDisplayPartKind { - AliasName, - ClassName, - EnumName, - EventName, - FieldName, - InterfaceName, - Keyword, - LabelName, - LineBreak, - NumericLiteral, - StringLiteral, - LocalName, - MethodName, - ModuleName, - NamespaceName, - Operator, - ParameterName, - PropertyName, - Punctuation, - Space, - AnonymousTypeIndicator, - Text, - TypeParameterName, - EnumMemberName, - FunctionName, - RegularExpressionLiteral, + aliasName, + className, + enumName, + fieldName, + interfaceName, + keyword, + labelName, + lineBreak, + numericLiteral, + stringLiteral, + localName, + methodName, + moduleName, + namespaceName, + operator, + parameterName, + propertyName, + punctuation, + space, + anonymousTypeIndicator, + text, + typeParameterName, + enumMemberName, + functionName, + regularExpressionLiteral, } export interface CancellationToken { From b43c123b53459062c8c2885c2ca27a27868b0b21 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 16:49:58 -0700 Subject: [PATCH 08/18] More quick info work. --- src/compiler/checker.ts | 2 +- src/services/services.ts | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1e7c96b64aa..9ef098832c8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -84,7 +84,7 @@ module ts { getTypeOfNode: getTypeOfNode, getApparentType: getApparentType, typeToString: typeToString, - typeToDisplayParts: undefined, + typeToDisplayParts: typeToDisplayParts, symbolToString: symbolToString, symbolToDisplayParts: symbolToDisplayParts, getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType, diff --git a/src/services/services.ts b/src/services/services.ts index aab25d76212..dd14c23e1e9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -665,7 +665,7 @@ module ts { kind: this.kind, kindModifiers: this.kindModifiers, textSpan: this.textSpan, - displayParts: this.displayParts.forEach(d => { + displayParts: this.displayParts.map(d => { return { text: d.text, kind: SymbolDisplayPartKind[d.kind] @@ -2255,11 +2255,33 @@ module ts { return undefined; } + var totalParts: SymbolDisplayPart[] = []; + if (symbol.flags & SymbolFlags.Class) { + totalParts.push({ text: "class", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + } + else if (symbol.flags & SymbolFlags.Interface) { + totalParts.push({ text: "interface", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + } + else if (symbol.flags & SymbolFlags.Enum) { + totalParts.push({ text: "enum", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + } + else if (symbol.flags & SymbolFlags.Module) { + totalParts.push({ text: "module", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + } + + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node))); + + var type = typeInfoResolver.getTypeOfSymbol(symbol); + if (type) { + totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type)); + } + return new QuickInfo( getSymbolKind(symbol), getSymbolModifiers(symbol), new TypeScript.TextSpan(node.getStart(), node.getWidth()), - typeInfoResolver.symbolToDisplayParts(symbol)); + totalParts); } function getTypeAtPosition(fileName: string, position: number): TypeInfo { From 82160d1a88e573dff482baffe2e5a92a9a793908 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 17:10:44 -0700 Subject: [PATCH 09/18] Tweak the appearance to match the old managed LS behavior. --- src/services/services.ts | 45 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index dd14c23e1e9..c9affa821e9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2255,26 +2255,63 @@ module ts { return undefined; } + // Having all this logic here is pretty unclean. Consider moving to the roslyn model + // where all symbol display logic is encapsulated into visitors and options. var totalParts: SymbolDisplayPart[] = []; + var addType = false; + if (symbol.flags & SymbolFlags.Class) { totalParts.push({ text: "class", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); } else if (symbol.flags & SymbolFlags.Interface) { totalParts.push({ text: "interface", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); } else if (symbol.flags & SymbolFlags.Enum) { totalParts.push({ text: "enum", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); } else if (symbol.flags & SymbolFlags.Module) { totalParts.push({ text: "module", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + } + else if (symbol.flags & SymbolFlags.TypeParameter) { + } + else { + addType = true; + totalParts.push({ text: "(", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + var text: string; + + if (symbol.flags & SymbolFlags.Property) { text = "property" } + else if (symbol.flags & SymbolFlags.EnumMember) { text = "enum member" } + else if (symbol.flags & SymbolFlags.Function) { text = "function" } + else if (symbol.flags & SymbolFlags.Variable) { text = "variable" } + else if (symbol.flags & SymbolFlags.Method) { text = "method" } + + if (!text) { + return undefined; + } + + totalParts.push({ text: text, kind: SymbolDisplayPartKind.text, symbol: undefined }); + totalParts.push({ text: ")", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); } - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node))); - var type = typeInfoResolver.getTypeOfSymbol(symbol); - if (type) { - totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type)); + if (symbol.flags & SymbolFlags.Property || + symbol.flags & SymbolFlags.EnumMember || + symbol.flags & SymbolFlags.Variable) { + + totalParts.push({ text: ".", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + } + + if (addType) { + var type = typeInfoResolver.getTypeOfSymbol(symbol); + if (type) { + totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type)); + } } return new QuickInfo( From 940951680aac60f06a11a17559f7d2e7114ba760 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 19 Sep 2014 17:33:25 -0700 Subject: [PATCH 10/18] Update quickinfo formatting. --- src/services/services.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index c9affa821e9..4a0f3d3e2ad 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2277,6 +2277,9 @@ module ts { totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); } else if (symbol.flags & SymbolFlags.TypeParameter) { + totalParts.push({ text: "(", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + totalParts.push({ text: "type parameter", kind: SymbolDisplayPartKind.text, symbol: undefined }); + totalParts.push({ text: ")", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); } else { addType = true; @@ -2304,7 +2307,8 @@ module ts { symbol.flags & SymbolFlags.EnumMember || symbol.flags & SymbolFlags.Variable) { - totalParts.push({ text: ".", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + totalParts.push({ text: ":", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); } if (addType) { From fae7a3560e5eabda677679afacb5a42c1ede02a5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 21 Sep 2014 13:58:15 -0700 Subject: [PATCH 11/18] Provide capability to ask the checker what the constant value of an enum member is. --- src/compiler/checker.ts | 98 ++++++++++++++++++++++++---------------- src/compiler/emitter.ts | 15 +++--- src/compiler/types.ts | 26 +++++++---- src/services/services.ts | 4 +- 4 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ba1005d01bd..30cbb800bc6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -90,7 +90,8 @@ module ts { getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType, getRootSymbol: getRootSymbol, getContextualType: getContextualType, - getFullyQualifiedName: getFullyQualifiedName + getFullyQualifiedName: getFullyQualifiedName, + getEnumMemberValue: getEnumMemberValue }; var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined"); @@ -6495,7 +6496,7 @@ module ts { } } - function getConstantValue(node: Expression): number { + function getConstantValueForExpression(node: Expression): number { var isNegative = false; if (node.kind === SyntaxKind.PrefixOperator) { var unaryExpression = node; @@ -6512,38 +6513,55 @@ module ts { return undefined; } + function computeEnumMemberValues(node: EnumDeclaration, reportErrors: boolean) { + var nodeLinks = getNodeLinks(node); + + if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputedAndChecked)) { + var enumSymbol = getSymbolOfNode(node); + var enumType = getDeclaredTypeOfSymbol(enumSymbol); + var autoValue = 0; + var ambient = isInAmbientContext(node); + + forEach(node.members, member => { + var initializer = member.initializer; + if (initializer) { + autoValue = getConstantValueForExpression(initializer); + if (autoValue === undefined && !ambient) { + // Only here do we need to check that the initializer is assignable to the enum type. + // If it is a constant value (not undefined), it is syntactically constrained to be a number. + // Also, we do not need to check this for ambients because there is already + // a syntax error if it is not a constant. + if (reportErrors) { + checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); + } + } + } + else if (ambient) { + autoValue = undefined; + } + + if (autoValue !== undefined) { + getNodeLinks(member).enumMemberValue = autoValue++; + } + }); + + if (reportErrors) { + nodeLinks.flags |= NodeCheckFlags.EnumValuesComputedAndChecked; + } + } + } + function checkEnumDeclaration(node: EnumDeclaration) { if (!fullTypeCheck) { return; } + checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); checkCollisionWithCapturedThisVariable(node, node.name); checkCollistionWithRequireExportsInGeneratedCode(node, node.name); checkExportsOnMergedDeclarations(node); - var enumSymbol = getSymbolOfNode(node); - var enumType = getDeclaredTypeOfSymbol(enumSymbol); - var autoValue = 0; - var ambient = isInAmbientContext(node); - forEach(node.members, member => { - var initializer = member.initializer; - if (initializer) { - autoValue = getConstantValue(initializer); - if (autoValue === undefined && !ambient) { - // Only here do we need to check that the initializer is assignable to the enum type. - // If it is a constant value (not undefined), it is syntactically constrained to be a number. - // Also, we do not need to check this for ambients because there is already - // a syntax error if it is not a constant. - checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); - } - } - else if (ambient) { - autoValue = undefined; - } - if (autoValue !== undefined) { - getNodeLinks(member).enumMemberValue = autoValue++; - } - }); + computeEnumMemberValues(node, /*reportErrors:*/ true); // Spec 2014 - Section 9.3: // It isn't possible for one enum declaration to continue the automatic numbering sequence of another, @@ -6551,6 +6569,7 @@ module ts { // for the first member. // // Only perform this check once per symbol + var enumSymbol = getSymbolOfNode(node); var firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind); if (node === firstDeclaration) { var seenEnumMissingInitialInitializer = false; @@ -7450,17 +7469,6 @@ module ts { } } - function getPropertyAccessSubstitution(node: PropertyAccess): string { - var symbol = getNodeLinks(node).resolvedSymbol; - if (symbol && (symbol.flags & SymbolFlags.EnumMember)) { - var declaration = symbol.valueDeclaration; - var constantValue: number; - if (declaration.kind === SyntaxKind.EnumMember && (constantValue = getNodeLinks(declaration).enumMemberValue) !== undefined) { - return constantValue.toString() + " /* " + identifierToString(declaration.name) + " */"; - } - } - } - function getExportAssignmentName(node: SourceFile): string { var symbol = getExportAssignmentSymbol(getSymbolOfNode(node)); return symbol && symbolIsValue(symbol) ? symbolToString(symbol): undefined; @@ -7523,9 +7531,23 @@ module ts { } function getEnumMemberValue(node: EnumMember): number { + computeEnumMemberValues(node.parent, /*reportErrors:*/ false); return getNodeLinks(node).enumMemberValue; } + function getConstantValue(node: PropertyAccess): number { + var symbol = getNodeLinks(node).resolvedSymbol; + if (symbol && (symbol.flags & SymbolFlags.EnumMember)) { + var declaration = symbol.valueDeclaration; + var constantValue: number; + if (declaration.kind === SyntaxKind.EnumMember && (constantValue = getNodeLinks(declaration).enumMemberValue) !== undefined) { + return constantValue; + } + } + + return undefined; + } + // Create a single instance that we can wrap the underlying emitter TextWriter with. That // way we don't have to allocate a new wrapper every time writeTypeAtLocation and // writeReturnTypeOfSignatureDeclaration are called. @@ -7561,7 +7583,6 @@ module ts { getProgram: () => program, getLocalNameOfContainer: getLocalNameOfContainer, getExpressionNamePrefix: getExpressionNamePrefix, - getPropertyAccessSubstitution: getPropertyAccessSubstitution, getExportAssignmentName: getExportAssignmentName, isReferencedImportDeclaration: isReferencedImportDeclaration, getNodeCheckFlags: getNodeCheckFlags, @@ -7573,7 +7594,8 @@ module ts { writeTypeAtLocation: writeTypeAtLocation, writeReturnTypeOfSignatureDeclaration: writeReturnTypeOfSignatureDeclaration, isSymbolAccessible: isSymbolAccessible, - isImportDeclarationEntityNameReferenceDeclarationVisibile: isImportDeclarationEntityNameReferenceDeclarationVisibile + isImportDeclarationEntityNameReferenceDeclarationVisibile: isImportDeclarationEntityNameReferenceDeclarationVisibile, + getConstantValue: getConstantValue, }; checkProgram(); return emitFiles(resolver, targetSourceFile); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b9229573472..663e15b2e3f 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -868,14 +868,15 @@ module ts { } function emitPropertyAccess(node: PropertyAccess) { - var text = resolver.getPropertyAccessSubstitution(node); - if (text) { - write(text); - return; + var constantValue = resolver.getConstantValue(node); + if (constantValue !== undefined) { + write(constantValue.toString() + " /* " + identifierToString(node.right) + " */"); + } + else { + emit(node.left); + write("."); + emit(node.right); } - emit(node.left); - write("."); - emit(node.right); } function emitIndexedAccess(node: IndexedAccess) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 09556ffc8de..5739778917e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -645,6 +645,10 @@ module ts { getAugmentedPropertiesOfApparentType(type: Type): Symbol[]; getRootSymbol(symbol: Symbol): Symbol; getContextualType(node: Node): Type; + + // Returns the constant value of this enum member, or 'undefined' if the enum member has a + // computed value. + getEnumMemberValue(node: EnumMember): number; } export interface TextWriter { @@ -680,7 +684,6 @@ module ts { getProgram(): Program; getLocalNameOfContainer(container: Declaration): string; getExpressionNamePrefix(node: Identifier): string; - getPropertyAccessSubstitution(node: PropertyAccess): string; getExportAssignmentName(node: SourceFile): string; isReferencedImportDeclaration(node: ImportDeclaration): boolean; isTopLevelValueImportedViaEntityName(node: ImportDeclaration): boolean; @@ -693,6 +696,10 @@ module ts { writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void; isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult; isImportDeclarationEntityNameReferenceDeclarationVisibile(entityName: EntityName): SymbolAccessiblityResult; + + // Returns the constant value this property access resolves to, or 'undefined' if it does + // resolve to a constant. + getConstantValue(node: PropertyAccess): number; } export enum SymbolFlags { @@ -794,13 +801,16 @@ module ts { } export enum NodeCheckFlags { - TypeChecked = 0x00000001, // Node has been type checked - LexicalThis = 0x00000002, // Lexical 'this' reference - CaptureThis = 0x00000004, // Lexical 'this' used in body - EmitExtends = 0x00000008, // Emit __extends - SuperInstance = 0x00000010, // Instance 'super' reference - SuperStatic = 0x00000020, // Static 'super' reference - ContextChecked = 0x00000040, // Contextual types have been assigned + TypeChecked = 0x00000001, // Node has been type checked + LexicalThis = 0x00000002, // Lexical 'this' reference + CaptureThis = 0x00000004, // Lexical 'this' used in body + EmitExtends = 0x00000008, // Emit __extends + SuperInstance = 0x00000010, // Instance 'super' reference + SuperStatic = 0x00000020, // Static 'super' reference + ContextChecked = 0x00000040, // Contextual types have been assigned + + // Values for enum members have been computed, and any errors have been reported for them. + EnumValuesComputedAndChecked = 0x00000080, } export interface NodeLinks { diff --git a/src/services/services.ts b/src/services/services.ts index 4a0f3d3e2ad..5d1fd8deb3d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2304,12 +2304,14 @@ module ts { totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node))); if (symbol.flags & SymbolFlags.Property || - symbol.flags & SymbolFlags.EnumMember || symbol.flags & SymbolFlags.Variable) { totalParts.push({ text: ":", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); } + else if (symbol.flags & SymbolFlags.EnumMember) { + + } if (addType) { var type = typeInfoResolver.getTypeOfSymbol(symbol); From 276a7353478162fce06869a4e50401c84f96f786 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 21 Sep 2014 14:25:09 -0700 Subject: [PATCH 12/18] Include the values of constant enums in quick-info. --- src/services/services.ts | 54 +++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 5d1fd8deb3d..d9e2b89034e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1491,11 +1491,14 @@ module ts { var formattingRulesProvider: TypeScript.Services.Formatting.RulesProvider; var hostCache: HostCache; // A cache of all the information about the files on the host side. var program: Program; + // this checker is used to answer all LS questions except errors var typeInfoResolver: TypeChecker; + // the sole purpose of this checker is to return semantic diagnostics // creation is deferred - use getFullTypeCheckChecker to get instance var fullTypeCheckChecker_doNotAccessDirectly: TypeChecker; + var useCaseSensitivefilenames = false; var sourceFilesByName: Map = {}; var documentRegistry = documentRegistry; @@ -2258,31 +2261,35 @@ module ts { // Having all this logic here is pretty unclean. Consider moving to the roslyn model // where all symbol display logic is encapsulated into visitors and options. var totalParts: SymbolDisplayPart[] = []; - var addType = false; if (symbol.flags & SymbolFlags.Class) { totalParts.push({ text: "class", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.Interface) { totalParts.push({ text: "interface", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.Enum) { totalParts.push({ text: "enum", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.Module) { totalParts.push({ text: "module", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.TypeParameter) { totalParts.push({ text: "(", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); totalParts.push({ text: "type parameter", kind: SymbolDisplayPartKind.text, symbol: undefined }); totalParts.push({ text: ")", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol)); } else { - addType = true; totalParts.push({ text: "(", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); var text: string; @@ -2299,24 +2306,37 @@ module ts { totalParts.push({ text: text, kind: SymbolDisplayPartKind.text, symbol: undefined }); totalParts.push({ text: ")", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); - } - totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node))); + totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node))); - if (symbol.flags & SymbolFlags.Property || - symbol.flags & SymbolFlags.Variable) { - - totalParts.push({ text: ":", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); - } - else if (symbol.flags & SymbolFlags.EnumMember) { - - } - - if (addType) { var type = typeInfoResolver.getTypeOfSymbol(symbol); - if (type) { - totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type)); + + if (symbol.flags & SymbolFlags.Property || + symbol.flags & SymbolFlags.Variable) { + + if (type) { + totalParts.push({ text: ":", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type, getContainerNode(node))); + } + } + else if (symbol.flags & SymbolFlags.Function || + symbol.flags & SymbolFlags.Method) { + if (type) { + totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type, getContainerNode(node))); + } + } + else if (symbol.flags & SymbolFlags.EnumMember) { + var declaration = symbol.declarations[0]; + if (declaration.kind === SyntaxKind.EnumMember) { + var constantValue = typeInfoResolver.getEnumMemberValue(declaration); + if (constantValue !== undefined) { + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push({ text: "=", kind: SymbolDisplayPartKind.operator, symbol: undefined }); + totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push({ text: constantValue.toString(), kind: SymbolDisplayPartKind.numericLiteral, symbol: undefined }); + } + } } } From e3609e20afe68a374f4b7970d18218cdabdf0abf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 21 Sep 2014 14:52:51 -0700 Subject: [PATCH 13/18] Rename Comment->CommentRange. --- src/compiler/emitter.ts | 28 +++++++++++++-------------- src/compiler/parser.ts | 14 ++++++++------ src/compiler/scanner.ts | 8 ++++---- src/compiler/types.ts | 2 +- src/services/services.ts | 41 ++++++++++++++++++++++++++++++++-------- 5 files changed, 60 insertions(+), 33 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 663e15b2e3f..47a6d9d7122 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -182,7 +182,7 @@ module ts { }); } - function emitComments(comments: Comment[], trailingSeparator: boolean, writer: EmitTextWriter, writeComment: (comment: Comment, writer: EmitTextWriter) => void) { + function emitComments(comments: CommentRange[], trailingSeparator: boolean, writer: EmitTextWriter, writeComment: (comment: CommentRange, writer: EmitTextWriter) => void) { var emitLeadingSpace = !trailingSeparator; forEach(comments, comment => { if (emitLeadingSpace) { @@ -203,7 +203,7 @@ module ts { }); } - function emitNewLineBeforeLeadingComments(node: TextRange, leadingComments: Comment[], writer: EmitTextWriter) { + function emitNewLineBeforeLeadingComments(node: TextRange, leadingComments: CommentRange[], writer: EmitTextWriter) { // If the leading comments start on different line than the start of node, write new line if (leadingComments && leadingComments.length && node.pos !== leadingComments[0].pos && getLineOfLocalPosition(node.pos) !== getLineOfLocalPosition(leadingComments[0].pos)) { @@ -211,7 +211,7 @@ module ts { } } - function writeCommentRange(comment: Comment, writer: EmitTextWriter) { + function writeCommentRange(comment: CommentRange, writer: EmitTextWriter) { if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) { var firstCommentLineAndCharacter = currentSourceFile.getLineAndCharacterFromPosition(comment.pos); var firstCommentLineIndent: number; @@ -585,7 +585,7 @@ module ts { sourceMapNameIndices.pop(); }; - function writeCommentRangeWithMap(comment: Comment, writer: EmitTextWriter) { + function writeCommentRangeWithMap(comment: CommentRange, writer: EmitTextWriter) { recordSourceMapSpan(comment.pos); writeCommentRange(comment, writer); recordSourceMapSpan(comment.end); @@ -2108,7 +2108,7 @@ module ts { function getLeadingCommentsWithoutDetachedComments() { // get the leading comments from detachedPos - var leadingComments = getLeadingComments(currentSourceFile.text, detachedCommentsInfo[detachedCommentsInfo.length - 1].detachedCommentEndPos); + var leadingComments = getLeadingCommentRanges(currentSourceFile.text, detachedCommentsInfo[detachedCommentsInfo.length - 1].detachedCommentEndPos); if (detachedCommentsInfo.length - 1) { detachedCommentsInfo.pop(); } @@ -2122,14 +2122,14 @@ module ts { function getLeadingCommentsToEmit(node: Node) { // Emit the leading comments only if the parent's pos doesn't match because parent should take care of emitting these comments if (node.parent.kind === SyntaxKind.SourceFile || node.pos !== node.parent.pos) { - var leadingComments: Comment[]; + var leadingComments: CommentRange[]; if (hasDetachedComments(node.pos)) { // get comments without detached comments leadingComments = getLeadingCommentsWithoutDetachedComments(); } else { // get the leading comments from the node - leadingComments = getLeadingCommentsOfNode(node, currentSourceFile); + leadingComments = getLeadingCommentRangesOfNode(node, currentSourceFile); } return leadingComments; } @@ -2145,21 +2145,21 @@ module ts { function emitTrailingDeclarationComments(node: Node) { // Emit the trailing comments only if the parent's end doesn't match if (node.parent.kind === SyntaxKind.SourceFile || node.end !== node.parent.end) { - var trailingComments = getTrailingComments(currentSourceFile.text, node.end); + var trailingComments = getTrailingCommentRanges(currentSourceFile.text, node.end); // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ emitComments(trailingComments, /*trailingSeparator*/ false, writer, writeComment); } } function emitLeadingCommentsOfLocalPosition(pos: number) { - var leadingComments: Comment[]; + var leadingComments: CommentRange[]; if (hasDetachedComments(pos)) { // get comments without detached comments leadingComments = getLeadingCommentsWithoutDetachedComments(); } else { // get the leading comments from the node - leadingComments = getLeadingComments(currentSourceFile.text, pos); + leadingComments = getLeadingCommentRanges(currentSourceFile.text, pos); } emitNewLineBeforeLeadingComments({ pos: pos, end: pos }, leadingComments, writer); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space @@ -2167,10 +2167,10 @@ module ts { } function emitDetachedCommentsAtPosition(node: TextRange) { - var leadingComments = getLeadingComments(currentSourceFile.text, node.pos); + var leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos); if (leadingComments) { - var detachedComments: Comment[] = []; - var lastComment: Comment; + var detachedComments: CommentRange[] = []; + var lastComment: CommentRange; forEach(leadingComments, comment => { if (lastComment) { @@ -2214,7 +2214,7 @@ module ts { function emitPinnedOrTripleSlashCommentsOfNode(node: Node) { var pinnedComments = ts.filter(getLeadingCommentsToEmit(node), isPinnedOrTripleSlashComment); - function isPinnedOrTripleSlashComment(comment: Comment) { + function isPinnedOrTripleSlashComment(comment: CommentRange) { if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) { return currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c735f1bb1e0..bc19efbdefc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -138,25 +138,27 @@ module ts { return ((node).expression).text === "use strict"; } - export function getLeadingCommentsOfNode(node: Node, sourceFileOfNode: SourceFile) { + export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode?: SourceFile) { + sourceFileOfNode = sourceFileOfNode || getSourceFileOfNode(node); + // If parameter/type parameter, the prev token trailing comments are part of this node too if (node.kind === SyntaxKind.Parameter || node.kind === SyntaxKind.TypeParameter) { // e.g. (/** blah */ a, /** blah */ b); - return concatenate(getTrailingComments(sourceFileOfNode.text, node.pos), + return concatenate(getTrailingCommentRanges(sourceFileOfNode.text, node.pos), // e.g.: ( // /** blah */ a, // /** blah */ b); - getLeadingComments(sourceFileOfNode.text, node.pos)); + getLeadingCommentRanges(sourceFileOfNode.text, node.pos)); } else { - return getLeadingComments(sourceFileOfNode.text, node.pos); + return getLeadingCommentRanges(sourceFileOfNode.text, node.pos); } } export function getJsDocComments(node: Declaration, sourceFileOfNode: SourceFile) { - return filter(getLeadingCommentsOfNode(node, sourceFileOfNode), comment => isJsDocComment(comment)); + return filter(getLeadingCommentRangesOfNode(node, sourceFileOfNode), comment => isJsDocComment(comment)); - function isJsDocComment(comment: Comment) { + function isJsDocComment(comment: CommentRange) { // True if the comment starts with '/**' but not if it is '/**/' return sourceFileOfNode.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && sourceFileOfNode.text.charCodeAt(comment.pos + 2) === CharacterCodes.asterisk && diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index f627bee6c51..81d16b5f487 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -371,8 +371,8 @@ module ts { // between the given position and the next line break are returned. The return value is an array containing a TextRange for each // comment. Single-line comment ranges include the beginning '//' characters but not the ending line break. Multi-line comment // ranges include the beginning '/* and ending '*/' characters. The return value is undefined if no comments were found. - function getCommentRanges(text: string, pos: number, trailing: boolean): Comment[] { - var result: Comment[]; + function getCommentRanges(text: string, pos: number, trailing: boolean): CommentRange[] { + var result: CommentRange[]; var collecting = trailing || pos === 0; while (true) { var ch = text.charCodeAt(pos); @@ -440,11 +440,11 @@ module ts { } } - export function getLeadingComments(text: string, pos: number): Comment[] { + export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] { return getCommentRanges(text, pos, /*trailing*/ false); } - export function getTrailingComments(text: string, pos: number): Comment[] { + export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] { return getCommentRanges(text, pos, /*trailing*/ true); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5739778917e..12409b60a79 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -529,7 +529,7 @@ module ts { filename: string; } - export interface Comment extends TextRange { + export interface CommentRange extends TextRange { hasTrailingNewLine?: boolean; } diff --git a/src/services/services.ts b/src/services/services.ts index d9e2b89034e..bf51918c678 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -97,9 +97,7 @@ module ts { private _children: Node[]; public getSourceFile(): SourceFile { - var node: Node = this; - while (node.kind !== SyntaxKind.SourceFile) node = node.parent; - return node; + return getSourceFileOfNode(this); } public getStart(sourceFile?: SourceFile): number { @@ -225,19 +223,44 @@ module ts { flags: SymbolFlags; name: string; declarations: Declaration[]; + documentationComment: string; + constructor(flags: SymbolFlags, name: string) { this.flags = flags; this.name = name; } + getFlags(): SymbolFlags { return this.flags; } + getName(): string { return this.name; } + getDeclarations(): Declaration[] { return this.declarations; } + + //getDocumentationComment(): string { + // if (this.documentationComment === undefined) { + // var result = ""; + + // var declarations = this.getDeclarations(); + // if (declarations) { + // for (var i = 0, n = declarations.length; i < n; i++) { + // var declaration = declarations[0]; + + // var commentRanges = getLeadingCommentRangesOfNode(declaration); + + // } + // } + + // this.documentationComment = result; + // } + + // return this.documentationComment; + //} } class TypeObject implements Type { @@ -657,7 +680,8 @@ module ts { constructor(public kind: string, public kindModifiers: string, public textSpan: TypeScript.TextSpan, - public displayParts: SymbolDisplayPart[]) { + public displayParts: SymbolDisplayPart[], + public documentation: SymbolDisplayPart[]) { } public toJSON() { @@ -2344,7 +2368,8 @@ module ts { getSymbolKind(symbol), getSymbolModifiers(symbol), new TypeScript.TextSpan(node.getStart(), node.getWidth()), - totalParts); + totalParts, + []/*convertDocumentation(symbol)*/); } function getTypeAtPosition(fileName: string, position: number): TypeInfo { @@ -4015,8 +4040,8 @@ module ts { } // Looks to be within the trivia. See if we can find the comment containing it. - if (!getContainingComment(getTrailingComments(fileContents, token.getFullStart()), matchPosition) && - !getContainingComment(getLeadingComments(fileContents, token.getFullStart()), matchPosition)) { + if (!getContainingComment(getTrailingCommentRanges(fileContents, token.getFullStart()), matchPosition) && + !getContainingComment(getLeadingCommentRanges(fileContents, token.getFullStart()), matchPosition)) { continue; } @@ -4103,7 +4128,7 @@ module ts { return new RegExp(regExpString, "gim"); } - function getContainingComment(comments: Comment[], position: number): Comment { + function getContainingComment(comments: CommentRange[], position: number): CommentRange { if (comments) { for (var i = 0, n = comments.length; i < n; i++) { var comment = comments[i]; From f4cdbfab18daee22564c5b19cbed72848589a0a8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Sep 2014 00:05:39 -0700 Subject: [PATCH 14/18] Add support for getting the doc comment from a symbol. --- src/compiler/checker.ts | 6 +- src/compiler/types.ts | 7 ++ src/services/services.ts | 200 +++++++++++++++++++++++++++++---------- 3 files changed, 158 insertions(+), 55 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30cbb800bc6..7f601963d2b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -950,12 +950,12 @@ module ts { var displayParts: SymbolDisplayPart[] = []; return { displayParts: () => displayParts, - writeKind: (text, kind) => displayParts.push({ text: text, kind: kind, symbol: undefined }), - writeSymbol: (text, symbol) => displayParts.push({ text: text, kind: displayPartKind(symbol), symbol: symbol }), + writeKind: (text, kind) => displayParts.push(new SymbolDisplayPart(text, kind, undefined)), + writeSymbol: (text, symbol) => displayParts.push(new SymbolDisplayPart(text, displayPartKind(symbol), symbol)), // Completely ignore indentation for display part writers. And map newlines to // a single space. - writeLine: () => displayParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }), + writeLine: () => displayParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)), increaseIndent: () => { }, decreaseIndent: () => { }, clear: () => displayParts = [], diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 12409b60a79..255ba4430fe 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1186,6 +1186,13 @@ module ts { public kind: SymbolDisplayPartKind, public symbol: Symbol) { } + + public toJSON() { + return { + text: this.text, + kind: SymbolDisplayPartKind[this.kind] + }; + } } export enum SymbolDisplayPartKind { diff --git a/src/services/services.ts b/src/services/services.ts index bf51918c678..d7c00876d39 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -242,25 +242,135 @@ module ts { return this.declarations; } - //getDocumentationComment(): string { - // if (this.documentationComment === undefined) { - // var result = ""; + getDocumentationComment(): string { + if (this.documentationComment === undefined) { + var lines: string[] = []; - // var declarations = this.getDeclarations(); - // if (declarations) { - // for (var i = 0, n = declarations.length; i < n; i++) { - // var declaration = declarations[0]; + // Get the doc comments from all the declarations of this symbol, and merge them + // into one single doc comment. + var declarations = this.getDeclarations(); + if (declarations) { + for (var i = 0, n = declarations.length; i < n; i++) { + this.processDocumentationCommentDeclaration(lines, declarations[0]); + } + } - // var commentRanges = getLeadingCommentRangesOfNode(declaration); + // TODO: get the newline info from the host. + this.documentationComment = lines.join("\r\n"); + } - // } - // } + return this.documentationComment; + } - // this.documentationComment = result; - // } + private processDocumentationCommentDeclaration(lines: string[], declaration: Node) { + var commentRanges = getLeadingCommentRangesOfNode(declaration); + var sourceFile = declaration.getSourceFile(); - // return this.documentationComment; - //} + for (var i = 0, n = commentRanges.length; i < n; i++) { + this.processDocumentationCommentRange( + lines, sourceFile, commentRanges[0]); + } + } + + private processDocumentationCommentRange(lines: string[], sourceFile: SourceFile, commentRange: CommentRange) { + // We only care about well-formed /** */ comments + if (commentRange.end - commentRange.pos > "/**/".length && + sourceFile.text.substr(commentRange.pos, "/**".length) === "/**" && + sourceFile.text.substr(commentRange.end - "*/".length, "*/".length) === "*/") { + + // Put a newline between each converted comment we join together. + if (lines.length) { + lines.push(""); + } + + var startLineAndChar = sourceFile.getLineAndCharacterFromPosition(commentRange.pos); + var endLineAndChar = sourceFile.getLineAndCharacterFromPosition(commentRange.end); + + if (startLineAndChar.line === endLineAndChar.line) { + // A single line doc comment. Just extract the text between the + // comment markers and add that to the doc comment we're building + // up. + lines.push(sourceFile.text.substring(commentRange.pos + "/**".length, commentRange.end - "*/".length).trim()); + } + else { + this.processMultiLineDocumentationCommentRange(sourceFile, commentRange, startLineAndChar, endLineAndChar, lines); + } + } + } + + private processMultiLineDocumentationCommentRange( + sourceFile: SourceFile, commentRange: CommentRange, + startLineAndChar: { line: number; character: number }, + endLineAndChar: { line: number; character: number }, + lines: string[]) { + + // Comment spanned multiple lines. Find the leftmost character + // position in each line, and use that to determine what we should + // trim off, and what part of the line to keep. + // i.e. if the comment looks like: + // + // /** Foo + // * Bar + // * Baz + // */ + // + // Then we'll want to add: + // Foo + // Bar + // Baz + var trimLength: number = undefined; + for (var iLine = startLineAndChar.line + 1; iLine <= endLineAndChar.line; iLine++) { + var lineStart = sourceFile.getPositionFromLineAndCharacter(iLine, /*character:*/ 0); + var docCommentTriviaLength = this.skipDocumentationCommentTrivia(sourceFile.text, lineStart); + + if (trimLength === undefined || (docCommentTriviaLength && docCommentTriviaLength < trimLength)) { + trimLength = docCommentTriviaLength; + } + } + + // Add the first line in. + var firstLine = this.trimRight(sourceFile.text.substr(commentRange.pos + "/**".length)); + if (firstLine !== "") { + lines.push(firstLine); + } + + // For all the lines up to the last (but not including the last), add the contents + // of the line (with the length up to the + for (var iLine = startLineAndChar.line + 1; iLine < endLineAndChar.line; iLine++) { + var line = this.trimRight(sourceFile.text.substring( + sourceFile.getPositionFromLineAndCharacter(iLine, /*character*/0), + sourceFile.getPositionFromLineAndCharacter(iLine + 1, /*character*/0))).substr(trimLength); + + lines.push(line); + } + + // Add the last line if there is any actual text before the */ + var lastLine = sourceFile.text.substring( + sourceFile.getPositionFromLineAndCharacter(endLineAndChar.line, /*character:*/ 0), + commentRange.end - "*/".length).substr(trimLength); + + if (lastLine !== "") { + lines.push(lastLine); + } + } + + private trimRight(val: string) { + return val.replace(/(\n|\r|\s)+$/, ''); + } + + private skipDocumentationCommentTrivia(text: string, lineStart: number): number { + for (var i = 0; i < text.length; i++) { + var char = text.charCodeAt(i); + if (char === CharacterCodes.asterisk) { + return i + 1; + } + else if (!isWhiteSpace(char)) { + return i; + } + } + + return undefined; + } } class TypeObject implements Type { @@ -683,20 +793,6 @@ module ts { public displayParts: SymbolDisplayPart[], public documentation: SymbolDisplayPart[]) { } - - public toJSON() { - return { - kind: this.kind, - kindModifiers: this.kindModifiers, - textSpan: this.textSpan, - displayParts: this.displayParts.map(d => { - return { - text: d.text, - kind: SymbolDisplayPartKind[d.kind] - }; - }) - }; - } } export class TypeInfo { @@ -2277,7 +2373,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfo(node); if (!symbol) { return undefined; } @@ -2287,34 +2383,34 @@ module ts { var totalParts: SymbolDisplayPart[] = []; if (symbol.flags & SymbolFlags.Class) { - totalParts.push({ text: "class", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push(new SymbolDisplayPart("class", SymbolDisplayPartKind.keyword, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.Interface) { - totalParts.push({ text: "interface", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push(new SymbolDisplayPart("interface", SymbolDisplayPartKind.keyword, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.Enum) { - totalParts.push({ text: "enum", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push(new SymbolDisplayPart("enum", SymbolDisplayPartKind.keyword, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.Module) { - totalParts.push({ text: "module", kind: SymbolDisplayPartKind.keyword, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push(new SymbolDisplayPart("module", SymbolDisplayPartKind.keyword, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } else if (symbol.flags & SymbolFlags.TypeParameter) { - totalParts.push({ text: "(", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); - totalParts.push({ text: "type parameter", kind: SymbolDisplayPartKind.text, symbol: undefined }); - totalParts.push({ text: ")", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push(new SymbolDisplayPart("(", SymbolDisplayPartKind.punctuation, undefined)); + totalParts.push(new SymbolDisplayPart("type parameter", SymbolDisplayPartKind.text, undefined)); + totalParts.push(new SymbolDisplayPart(")", SymbolDisplayPartKind.punctuation, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol)); } else { - totalParts.push({ text: "(", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); + totalParts.push(new SymbolDisplayPart("(", SymbolDisplayPartKind.punctuation, undefined)); var text: string; if (symbol.flags & SymbolFlags.Property) { text = "property" } @@ -2327,9 +2423,9 @@ module ts { return undefined; } - totalParts.push({ text: text, kind: SymbolDisplayPartKind.text, symbol: undefined }); - totalParts.push({ text: ")", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push(new SymbolDisplayPart(text, SymbolDisplayPartKind.text, undefined)); + totalParts.push(new SymbolDisplayPart(")", SymbolDisplayPartKind.punctuation, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node))); @@ -2339,8 +2435,8 @@ module ts { symbol.flags & SymbolFlags.Variable) { if (type) { - totalParts.push({ text: ":", kind: SymbolDisplayPartKind.punctuation, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); + totalParts.push(new SymbolDisplayPart(":", SymbolDisplayPartKind.punctuation, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type, getContainerNode(node))); } } @@ -2355,10 +2451,10 @@ module ts { if (declaration.kind === SyntaxKind.EnumMember) { var constantValue = typeInfoResolver.getEnumMemberValue(declaration); if (constantValue !== undefined) { - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); - totalParts.push({ text: "=", kind: SymbolDisplayPartKind.operator, symbol: undefined }); - totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined }); - totalParts.push({ text: constantValue.toString(), kind: SymbolDisplayPartKind.numericLiteral, symbol: undefined }); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); + totalParts.push(new SymbolDisplayPart("=", SymbolDisplayPartKind.operator, undefined)); + totalParts.push(new SymbolDisplayPart(" ", SymbolDisplayPartKind.space, undefined)); + totalParts.push(new SymbolDisplayPart(constantValue.toString(), SymbolDisplayPartKind.numericLiteral, undefined)); } } } @@ -2369,7 +2465,7 @@ module ts { getSymbolModifiers(symbol), new TypeScript.TextSpan(node.getStart(), node.getWidth()), totalParts, - []/*convertDocumentation(symbol)*/); + [new SymbolDisplayPart(symbol.getDocumentationComment(), SymbolDisplayPartKind.text, /*symbol:*/ null)]); } function getTypeAtPosition(fileName: string, position: number): TypeInfo { From cfac0be7b8bf9d763fcffb7c6e8a5587f3397de3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Sep 2014 01:26:52 -0700 Subject: [PATCH 15/18] Show documentation comments in quick info. --- src/services/services.ts | 61 +++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index d7c00876d39..44b9fb16411 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -264,11 +264,13 @@ module ts { private processDocumentationCommentDeclaration(lines: string[], declaration: Node) { var commentRanges = getLeadingCommentRangesOfNode(declaration); - var sourceFile = declaration.getSourceFile(); + if (commentRanges) { + var sourceFile = declaration.getSourceFile(); - for (var i = 0, n = commentRanges.length; i < n; i++) { - this.processDocumentationCommentRange( - lines, sourceFile, commentRanges[0]); + for (var i = 0, n = commentRanges.length; i < n; i++) { + this.processDocumentationCommentRange( + lines, sourceFile, commentRanges[0]); + } } } @@ -320,8 +322,11 @@ module ts { // Baz var trimLength: number = undefined; for (var iLine = startLineAndChar.line + 1; iLine <= endLineAndChar.line; iLine++) { - var lineStart = sourceFile.getPositionFromLineAndCharacter(iLine, /*character:*/ 0); - var docCommentTriviaLength = this.skipDocumentationCommentTrivia(sourceFile.text, lineStart); + var lineStart = sourceFile.getPositionFromLineAndCharacter(iLine, /*character:*/ 1); + var lineEnd = iLine === endLineAndChar.line + ? commentRange.end - "*/".length + : sourceFile.getPositionFromLineAndCharacter(iLine + 1, 1); + var docCommentTriviaLength = this.skipDocumentationCommentTrivia(sourceFile.text, lineStart, lineEnd); if (trimLength === undefined || (docCommentTriviaLength && docCommentTriviaLength < trimLength)) { trimLength = docCommentTriviaLength; @@ -329,7 +334,9 @@ module ts { } // Add the first line in. - var firstLine = this.trimRight(sourceFile.text.substr(commentRange.pos + "/**".length)); + var firstLine = sourceFile.text.substring( + commentRange.pos + "/**".length, + sourceFile.getPositionFromLineAndCharacter(startLineAndChar.line + 1, /*character:*/ 1)).trim(); if (firstLine !== "") { lines.push(firstLine); } @@ -338,16 +345,16 @@ module ts { // of the line (with the length up to the for (var iLine = startLineAndChar.line + 1; iLine < endLineAndChar.line; iLine++) { var line = this.trimRight(sourceFile.text.substring( - sourceFile.getPositionFromLineAndCharacter(iLine, /*character*/0), - sourceFile.getPositionFromLineAndCharacter(iLine + 1, /*character*/0))).substr(trimLength); + sourceFile.getPositionFromLineAndCharacter(iLine, /*character*/ 1), + sourceFile.getPositionFromLineAndCharacter(iLine + 1, /*character*/ 1))).substr(trimLength); lines.push(line); } // Add the last line if there is any actual text before the */ - var lastLine = sourceFile.text.substring( - sourceFile.getPositionFromLineAndCharacter(endLineAndChar.line, /*character:*/ 0), - commentRange.end - "*/".length).substr(trimLength); + var lastLine = this.trimRight(sourceFile.text.substring( + sourceFile.getPositionFromLineAndCharacter(endLineAndChar.line, /*character:*/ 1), + commentRange.end - "*/".length)).substr(trimLength); if (lastLine !== "") { lines.push(lastLine); @@ -358,13 +365,25 @@ module ts { return val.replace(/(\n|\r|\s)+$/, ''); } - private skipDocumentationCommentTrivia(text: string, lineStart: number): number { - for (var i = 0; i < text.length; i++) { - var char = text.charCodeAt(i); - if (char === CharacterCodes.asterisk) { - return i + 1; + private skipDocumentationCommentTrivia(text: string, lineStart: number, lineEnd: number): number { + var seenAsterisk = false; + var lineLength = lineEnd - lineStart; + for (var i = 0; i < lineLength; i++) { + var char = text.charCodeAt(i + lineStart); + if (char === CharacterCodes.asterisk && !seenAsterisk) { + // Ignore the first asterisk we see. We want to trim out the line of *'s + // commonly seen at the start of a doc comment. + seenAsterisk = true; + continue; + } + else if (isLineBreak(char)) { + // This was a blank line. Just ignore it wrt computing the leading whitespace to + // trim. + break; } else if (!isWhiteSpace(char)) { + // Found a real doc comment character. Keep track of it so we can determine how + // much of the doc comment leading trivia to trim off. return i; } } @@ -2362,10 +2381,9 @@ module ts { return result.length > 0 ? result.join(',') : ScriptElementKindModifier.none; } - /// QuickInfo function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo { synchronizeHostData(); - + fileName = TypeScript.switchToForwardSlashes(fileName); var sourceFile = getSourceFile(fileName); var node = getNodeAtPosition(sourceFile, position); @@ -2378,6 +2396,9 @@ module ts { return undefined; } + var documentation = symbol.getDocumentationComment(); + var documentationParts = documentation === "" ? [] : [new SymbolDisplayPart(documentation, SymbolDisplayPartKind.text, /*symbol:*/ null)]; + // Having all this logic here is pretty unclean. Consider moving to the roslyn model // where all symbol display logic is encapsulated into visitors and options. var totalParts: SymbolDisplayPart[] = []; @@ -2465,7 +2486,7 @@ module ts { getSymbolModifiers(symbol), new TypeScript.TextSpan(node.getStart(), node.getWidth()), totalParts, - [new SymbolDisplayPart(symbol.getDocumentationComment(), SymbolDisplayPartKind.text, /*symbol:*/ null)]); + documentationParts); } function getTypeAtPosition(fileName: string, position: number): TypeInfo { From 7a4fb642333c9ce0cf54cc893678b2f93730e837 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 23 Sep 2014 13:32:32 -0700 Subject: [PATCH 16/18] Code review feedback. --- src/compiler/checker.ts | 18 ++++++++---------- src/compiler/types.ts | 16 ++++++++-------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f601963d2b..55255abb1eb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -972,8 +972,8 @@ module ts { return { string: () => str, - writeKind: (text, kind) => str += text, - writeSymbol: (text, symbol) => str += text, + writeKind: text => str += text, + writeSymbol: text => str += text, // Completely ignore indentation for string writers. And map newlines to // a single space. @@ -6513,10 +6513,10 @@ module ts { return undefined; } - function computeEnumMemberValues(node: EnumDeclaration, reportErrors: boolean) { + function computeEnumMemberValues(node: EnumDeclaration) { var nodeLinks = getNodeLinks(node); - if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputedAndChecked)) { + if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { var enumSymbol = getSymbolOfNode(node); var enumType = getDeclaredTypeOfSymbol(enumSymbol); var autoValue = 0; @@ -6531,7 +6531,7 @@ module ts { // If it is a constant value (not undefined), it is syntactically constrained to be a number. // Also, we do not need to check this for ambients because there is already // a syntax error if it is not a constant. - if (reportErrors) { + if (fullTypeCheck) { checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); } } @@ -6545,9 +6545,7 @@ module ts { } }); - if (reportErrors) { - nodeLinks.flags |= NodeCheckFlags.EnumValuesComputedAndChecked; - } + nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; } } @@ -6561,7 +6559,7 @@ module ts { checkCollistionWithRequireExportsInGeneratedCode(node, node.name); checkExportsOnMergedDeclarations(node); - computeEnumMemberValues(node, /*reportErrors:*/ true); + computeEnumMemberValues(node); // Spec 2014 - Section 9.3: // It isn't possible for one enum declaration to continue the automatic numbering sequence of another, @@ -7531,7 +7529,7 @@ module ts { } function getEnumMemberValue(node: EnumMember): number { - computeEnumMemberValues(node.parent, /*reportErrors:*/ false); + computeEnumMemberValues(node.parent); return getNodeLinks(node).enumMemberValue; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 255ba4430fe..0d8a4ecc4df 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -801,16 +801,16 @@ module ts { } export enum NodeCheckFlags { - TypeChecked = 0x00000001, // Node has been type checked - LexicalThis = 0x00000002, // Lexical 'this' reference - CaptureThis = 0x00000004, // Lexical 'this' used in body - EmitExtends = 0x00000008, // Emit __extends - SuperInstance = 0x00000010, // Instance 'super' reference - SuperStatic = 0x00000020, // Static 'super' reference - ContextChecked = 0x00000040, // Contextual types have been assigned + TypeChecked = 0x00000001, // Node has been type checked + LexicalThis = 0x00000002, // Lexical 'this' reference + CaptureThis = 0x00000004, // Lexical 'this' used in body + EmitExtends = 0x00000008, // Emit __extends + SuperInstance = 0x00000010, // Instance 'super' reference + SuperStatic = 0x00000020, // Static 'super' reference + ContextChecked = 0x00000040, // Contextual types have been assigned // Values for enum members have been computed, and any errors have been reported for them. - EnumValuesComputedAndChecked = 0x00000080, + EnumValuesComputed = 0x00000080, } export interface NodeLinks { From 3c68252e08cddf28aa6dfbf6de5f40ead0526f8e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 23 Sep 2014 13:51:42 -0700 Subject: [PATCH 17/18] Simplify writing code. --- src/compiler/checker.ts | 167 ++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 77 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 55255abb1eb..b30128af96f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -998,6 +998,22 @@ module ts { stringWriters.push(writer); } + function writeKeyword(writer: SymbolWriter, kind: SyntaxKind) { + writer.writeKind(tokenToString(kind), SymbolDisplayPartKind.keyword); + } + + function writePunctuation(writer: SymbolWriter, kind: SyntaxKind) { + writer.writeKind(tokenToString(kind), SymbolDisplayPartKind.punctuation); + } + + function writeOperator(writer: SymbolWriter, kind: SyntaxKind) { + writer.writeKind(tokenToString(kind), SymbolDisplayPartKind.operator); + } + + function writeSpace(writer: SymbolWriter) { + writer.writeKind(" ", SymbolDisplayPartKind.space); + } + function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string { var writer = getStringWriter(); writeSymbol(symbol, writer, enclosingDeclaration, meaning); @@ -1059,7 +1075,7 @@ module ts { if (accessibleSymbolChain) { for (var i = 0, n = accessibleSymbolChain.length; i < n; i++) { if (needsDot) { - writer.writeKind(".", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.DotToken); } writeSymbolName(accessibleSymbolChain[i]); @@ -1073,7 +1089,7 @@ module ts { } if (needsDot) { - writer.writeKind(".", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.DotToken); } writeSymbolName(symbol); @@ -1100,7 +1116,7 @@ module ts { function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { var writer = getStringWriter(); - writeType(type, enclosingDeclaration, flags, writer); + writeType(type, writer, enclosingDeclaration, flags); var result = writer.string(); releaseStringWriter(writer); @@ -1115,7 +1131,7 @@ module ts { function typeToDisplayParts(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] { var writer = getDisplayPartWriter(); - writeType(type, enclosingDeclaration, flags, writer); + writeType(type, writer, enclosingDeclaration, flags); var result = writer.displayParts(); releaseDisplayPartWriter(writer); @@ -1123,14 +1139,7 @@ module ts { return result; } - function writeType(type: Type, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void { - //var maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100; - //var stringWriter = createSingleLineTextWriter(maxLength); - // TODO(shkamat): typeToString should take enclosingDeclaration as input, once we have implemented enclosingDeclaration - writeTypeToWriter(type, writer, enclosingDeclaration, flags); - } - - function writeTypeToWriter(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) { + function writeType(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) { var typeStack: Type[]; return writeType(type, /*allowFunctionOrConstructorTypeLiteral*/ true); @@ -1156,19 +1165,19 @@ module ts { else { // Should never get here // { ... } - writer.writeKind("{", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); - writer.writeKind("...", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); - writer.writeKind("}", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenBraceToken); + writeSpace(writer); + writePunctuation(writer, SyntaxKind.DotDotDotToken); + writeSpace(writer); + writePunctuation(writer, SyntaxKind.CloseBraceToken); } } function writeTypeList(types: Type[]) { for (var i = 0; i < types.length; i++) { if (i > 0) { - writer.writeKind(",", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writePunctuation(writer, SyntaxKind.CommaToken); + writeSpace(writer); } writeType(types[i], /*allowFunctionOrConstructorTypeLiteral*/ true); } @@ -1179,20 +1188,21 @@ module ts { // If we are writing array element type the arrow style signatures are not allowed as // we need to surround it by curlies, e.g. { (): T; }[]; as () => T[] would mean something different writeType(type.typeArguments[0], /*allowFunctionOrConstructorTypeLiteral*/ false); - writer.writeKind("[]", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenBracketToken); + writePunctuation(writer, SyntaxKind.CloseBracketToken); } else { writeSymbol(type.target.symbol, writer, enclosingDeclaration, SymbolFlags.Type); - writer.writeKind("<", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.LessThanToken); writeTypeList(type.typeArguments); - writer.writeKind(">", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.GreaterThanToken); } } function writeTupleType(type: TupleType) { - writer.writeKind("[", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenBracketToken); writeTypeList(type.elementTypes); - writer.writeKind("]", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.CloseBracketToken); } function writeAnonymousType(type: ObjectType, allowFunctionOrConstructorTypeLiteral: boolean) { @@ -1206,7 +1216,7 @@ module ts { } else if (typeStack && contains(typeStack, type)) { // Recursive usage, use any - writer.writeKind("any", SymbolDisplayPartKind.keyword); + writeKeyword(writer, SyntaxKind.AnyKeyword); } else { if (!typeStack) { @@ -1236,8 +1246,8 @@ module ts { } function writeTypeofSymbol(type: ObjectType) { - writer.writeKind("typeof", SymbolDisplayPartKind.keyword); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writeKeyword(writer, SyntaxKind.TypeOfKeyword); + writeSpace(writer); writeSymbol(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value); } @@ -1245,7 +1255,8 @@ module ts { var resolved = resolveObjectTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexType && !resolved.numberIndexType) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { - writer.writeKind("{}", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenBraceToken); + writePunctuation(writer, SyntaxKind.CloseBraceToken); return; } @@ -1255,54 +1266,56 @@ module ts { return; } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { - writer.writeKind("new", SymbolDisplayPartKind.keyword); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writeKeyword(writer, SyntaxKind.NewKeyword); + writeSpace(writer); writeSignature(resolved.constructSignatures[0], /*arrowStyle*/ true); return; } } } - writer.writeKind("{", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); for (var i = 0; i < resolved.callSignatures.length; i++) { writeSignature(resolved.callSignatures[i]); - writer.writeKind(";", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } for (var i = 0; i < resolved.constructSignatures.length; i++) { - writer.writeKind("new", SymbolDisplayPartKind.keyword); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writeKeyword(writer, SyntaxKind.NewKeyword); + writeSpace(writer); writeSignature(resolved.constructSignatures[i]); - writer.writeKind(";", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } if (resolved.stringIndexType) { // [x: string]: - writer.writeKind("[", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenBracketToken); writer.writeKind("x", SymbolDisplayPartKind.parameterName); - writer.writeKind(":", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); - writer.writeKind("string", SymbolDisplayPartKind.keyword); - writer.writeKind("]:", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); + writeKeyword(writer, SyntaxKind.StringKeyword); + writePunctuation(writer, SyntaxKind.CloseBracketToken); + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); writeType(resolved.stringIndexType, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.writeKind(";", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } if (resolved.numberIndexType) { // [x: number]: - writer.writeKind("[", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenBracketToken); writer.writeKind("x", SymbolDisplayPartKind.parameterName); - writer.writeKind(":", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); - writer.writeKind("number", SymbolDisplayPartKind.keyword); - writer.writeKind("]:", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); + writeKeyword(writer, SyntaxKind.NumberKeyword); + writePunctuation(writer, SyntaxKind.CloseBracketToken); + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); writeType(resolved.numberIndexType, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.writeKind(";", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } for (var i = 0; i < resolved.properties.length; i++) { @@ -1313,78 +1326,78 @@ module ts { for (var j = 0; j < signatures.length; j++) { writeSymbol(p, writer); if (isOptionalProperty(p)) { - writer.writeKind("?", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.QuestionToken); } writeSignature(signatures[j]); - writer.writeKind(";", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } } else { writeSymbol(p, writer); if (isOptionalProperty(p)) { - writer.writeKind("?", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.QuestionToken); } - writer.writeKind(":", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); writeType(t, /*allowFunctionOrConstructorTypeLiteral*/ true); - writer.writeKind(";", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } } writer.decreaseIndent(); - writer.writeKind("}", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.CloseBraceToken); } function writeSignature(signature: Signature, arrowStyle?: boolean) { if (signature.typeParameters) { - writer.writeKind("<", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.LessThanToken); for (var i = 0; i < signature.typeParameters.length; i++) { if (i > 0) { - writer.writeKind(",", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writePunctuation(writer, SyntaxKind.CommaToken); + writeSpace(writer); } var tp = signature.typeParameters[i]; writeSymbol(tp.symbol, writer); var constraint = getConstraintOfTypeParameter(tp); if (constraint) { - writer.writeKind(" ", SymbolDisplayPartKind.space); - writer.writeKind("extends", SymbolDisplayPartKind.keyword); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writeSpace(writer); + writeKeyword(writer, SyntaxKind.ExtendsKeyword); + writeSpace(writer); writeType(constraint, /*allowFunctionOrConstructorTypeLiteral*/ true); } } - writer.writeKind(">", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.GreaterThanToken); } - writer.writeKind("(", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.OpenParenToken); for (var i = 0; i < signature.parameters.length; i++) { if (i > 0) { - writer.writeKind(",", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writePunctuation(writer, SyntaxKind.CommaToken); + writeSpace(writer); } var p = signature.parameters[i]; if (getDeclarationFlagsFromSymbol(p) & NodeFlags.Rest) { - writer.writeKind("...", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.DotDotDotToken); } writeSymbol(p, writer); if (p.valueDeclaration.flags & NodeFlags.QuestionMark || (p.valueDeclaration).initializer) { - writer.writeKind("?", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.QuestionToken); } - writer.writeKind(":", SymbolDisplayPartKind.punctuation); - writer.writeKind(" ", SymbolDisplayPartKind.space); + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); writeType(getTypeOfSymbol(p), /*allowFunctionOrConstructorTypeLiteral*/ true); } - writer.writeKind(")", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.CloseParenToken); if (arrowStyle) { - writer.writeKind(" ", SymbolDisplayPartKind.space); - writer.writeKind("=>", SymbolDisplayPartKind.punctuation); + writeSpace(writer); + writePunctuation(writer, SyntaxKind.EqualsGreaterThanToken); } else { - writer.writeKind(":", SymbolDisplayPartKind.punctuation); + writePunctuation(writer, SyntaxKind.ColonToken); } - writer.writeKind(" ", SymbolDisplayPartKind.space); + writeSpace(writer); writeType(getReturnTypeOfSignature(signature), /*allowFunctionOrConstructorTypeLiteral*/ true); } @@ -7567,13 +7580,13 @@ module ts { var type = symbol && !(symbol.flags & SymbolFlags.TypeLiteral) ? getTypeOfSymbol(symbol) : getTypeFromTypeNode(location); emitSymbolWriter.writer = writer; - writeTypeToWriter(type, emitSymbolWriter, enclosingDeclaration, flags); + writeType(type, emitSymbolWriter, enclosingDeclaration, flags); } function writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter) { var signature = getSignatureFromDeclaration(signatureDeclaration); emitSymbolWriter.writer = writer; - writeTypeToWriter(getReturnTypeOfSignature(signature), emitSymbolWriter, enclosingDeclaration, flags); + writeType(getReturnTypeOfSignature(signature), emitSymbolWriter, enclosingDeclaration, flags); } function invokeEmitter(targetSourceFile?: SourceFile) { From d6fc3fc2daaf9cd91fcc69f79d626d421ec0b84f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 23 Sep 2014 15:41:04 -0700 Subject: [PATCH 18/18] Code review feedback. --- src/compiler/checker.ts | 4 +--- src/services/services.ts | 6 +++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b30128af96f..227e99b712d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6544,9 +6544,7 @@ module ts { // If it is a constant value (not undefined), it is syntactically constrained to be a number. // Also, we do not need to check this for ambients because there is already // a syntax error if it is not a constant. - if (fullTypeCheck) { - checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); - } + checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined); } } else if (ambient) { diff --git a/src/services/services.ts b/src/services/services.ts index adaeed119c9..8282677ffd4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -45,6 +45,7 @@ module ts { getFlags(): SymbolFlags; getName(): string; getDeclarations(): Declaration[]; + getDocumentationComment(): string; } export interface Type { @@ -222,6 +223,9 @@ module ts { flags: SymbolFlags; name: string; declarations: Declaration[]; + + // Undefined is used to indicate the value has not been computed. If, after computing, the + // symbol has no doc comment, then the empty string will be returned. documentationComment: string; constructor(flags: SymbolFlags, name: string) { @@ -2390,7 +2394,7 @@ module ts { return undefined; } - var symbol = typeInfoResolver.getSymbolInfo(node); + var symbol = typeInfoResolver.getSymbolInfo(node); if (!symbol) { return undefined; }