diff --git a/Jakefile b/Jakefile index 4f2808dcbd1..74f950be9e0 100644 --- a/Jakefile +++ b/Jakefile @@ -58,7 +58,8 @@ var servicesSources = [ "shims.ts", "signatureHelp.ts", "utilities.ts", - "navigationBar.ts" + "navigationBar.ts", + "outliningElementsCollector.ts" ].map(function (f) { return path.join(servicesDirectory, f); })); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ab6279f99d6..1e1c57f5700 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3231,21 +3231,27 @@ module ts { } if (targetSourceFile === undefined) { + // No targetSourceFile is specified (e.g. calling emitter from batch compiler) forEach(program.getSourceFiles(), sourceFile => { if (shouldEmitToOwnFile(sourceFile, compilerOptions)) { var jsFilePath = getOwnEmitOutputFilePath(sourceFile, ".js"); emitFile(jsFilePath, sourceFile); } }); - } - else { - // Emit only one file specified in targetFilename. This is mainly used in compilerOnSave feature - var jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, ".js"); - emitFile(jsFilePath, targetSourceFile); - } - if (compilerOptions.out) { - emitFile(compilerOptions.out); + if (compilerOptions.out) { + emitFile(compilerOptions.out); + } + } else { + // targetSourceFile is specified (e.g calling emitter from language service) + if (shouldEmitToOwnFile(targetSourceFile, compilerOptions)) { + // If shouldEmitToOwnFile is true or targetSourceFile is an external module file, then emit targetSourceFile in its own output file + var jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, ".js"); + emitFile(jsFilePath, targetSourceFile); + } else { + // If shouldEmitToOwnFile is false, then emit all, non-external-module file, into one single output file + emitFile(compilerOptions.out); + } } // Sort and make the unique list of diagnostics diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index ef2b5e9334a..9f16fa57741 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -148,7 +148,7 @@ module ts.NavigationBar { var item = createItem(child); if (item !== undefined) { if (item.text.length > 0) { - var key = item.text + "-" + item.kind; + var key = item.text + "-" + item.kind + "-" + item.indent; var itemWithSameName = keyToItem[key]; if (itemWithSameName) { diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index c9a513ee2aa..aaaedb735ea 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -33,13 +33,14 @@ module ts { export module OutliningElementsCollector { export function collectElements(sourceFile: SourceFile): OutliningSpan[] { var elements: OutliningSpan[] = []; + var collapseText = "..."; function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean) { if (hintSpanNode && startElement && endElement) { var span: OutliningSpan = { textSpan: TypeScript.TextSpan.fromBounds(startElement.pos, endElement.end), hintSpan: TypeScript.TextSpan.fromBounds(hintSpanNode.getStart(), hintSpanNode.end), - bannerText: "...", + bannerText: collapseText, autoCollapse: autoCollapse }; elements.push(span); @@ -66,10 +67,39 @@ module ts { } switch (n.kind) { case SyntaxKind.Block: + var parent = n.parent; + var openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); + var closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); + + // Check if the block is standalone, or 'attached' to some parent statement. + // If the latter, we want to collaps the block, but consider its hint span + // to be the entire span of the parent. + if (parent.kind === SyntaxKind.DoStatement || + parent.kind === SyntaxKind.ForInStatement || + parent.kind === SyntaxKind.ForStatement || + parent.kind === SyntaxKind.IfStatement || + parent.kind === SyntaxKind.WhileStatement || + parent.kind === SyntaxKind.WithStatement) { + + addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n)); + } + else { + // Block was a standalone block. In this case we want to only collapse + // the span of the block, independent of any parent span. + var span = TypeScript.TextSpan.fromBounds(n.getStart(), n.end); + elements.push({ + textSpan: span, + hintSpan: span, + bannerText: collapseText, + autoCollapse: autoCollapse(n) + }); + } + break; + + case SyntaxKind.FunctionBlock: case SyntaxKind.ModuleBlock: case SyntaxKind.TryBlock: - case SyntaxKind.TryBlock: case SyntaxKind.CatchBlock: case SyntaxKind.FinallyBlock: var openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); diff --git a/src/services/services.ts b/src/services/services.ts index e4d28f4700f..703a05c62cb 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -504,27 +504,47 @@ module ts { if (!this.namedDeclarations) { var sourceFile = this; var namedDeclarations: Declaration[] = []; - var isExternalModule = ts.isExternalModule(sourceFile); - forEachChild(sourceFile, function visit(node: Node): boolean { + forEachChild(sourceFile, function visit(node: Node): void { switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.Method: + var functionDeclaration = node; + + if (functionDeclaration.name && functionDeclaration.name.kind !== SyntaxKind.Missing) { + var lastDeclaration = namedDeclarations.length > 0 ? + namedDeclarations[namedDeclarations.length - 1] : + undefined; + + // Check whether this declaration belongs to an "overload group". + if (lastDeclaration && functionDeclaration.symbol === lastDeclaration.symbol) { + // Overwrite the last declaration if it was an overload + // and this one is an implementation. + if (functionDeclaration.body && !(lastDeclaration).body) { + namedDeclarations[namedDeclarations.length - 1] = functionDeclaration; + } + } + else { + namedDeclarations.push(node); + } + + forEachChild(node, visit); + } + break; + case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.ImportDeclaration: - case SyntaxKind.Method: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.TypeLiteral: if ((node).name) { namedDeclarations.push(node); } - forEachChild(node, visit); - break; - + // fall through + case SyntaxKind.Constructor: case SyntaxKind.VariableStatement: case SyntaxKind.ModuleBlock: case SyntaxKind.FunctionBlock: @@ -532,19 +552,17 @@ module ts { break; case SyntaxKind.Parameter: + // Only consider properties defined as constructor parameters if (!(node.flags & NodeFlags.AccessibilityModifier)) { - // Only consider properties defined as constructor parameters break; } + // fall through case SyntaxKind.VariableDeclaration: case SyntaxKind.EnumMember: case SyntaxKind.Property: namedDeclarations.push(node); break; } - - // do not go any deeper - return undefined; }); this.namedDeclarations = namedDeclarations; @@ -3882,7 +3900,8 @@ module ts { filename = TypeScript.switchToForwardSlashes(filename); var compilerOptions = program.getCompilerOptions(); var targetSourceFile = program.getSourceFile(filename); // Current selected file to be output - var emitToSingleFile = ts.shouldEmitToOwnFile(targetSourceFile, compilerOptions); + // If --out flag is not specified, shouldEmitToOwnFile is true. Otherwise shouldEmitToOwnFile is false. + var shouldEmitToOwnFile = ts.shouldEmitToOwnFile(targetSourceFile, compilerOptions); var emitDeclaration = compilerOptions.declaration; var emitOutput: EmitOutput = { outputFiles: [], @@ -3903,7 +3922,7 @@ module ts { var syntacticDiagnostics: Diagnostic[] = []; var containSyntacticErrors = false; - if (emitToSingleFile) { + if (shouldEmitToOwnFile) { // Check only the file we want to emit containSyntacticErrors = containErrors(program.getDiagnostics(targetSourceFile)); } else { @@ -3930,7 +3949,7 @@ module ts { // Perform semantic and force a type check before emit to ensure that all symbols are updated // EmitFiles will report if there is an error from TypeChecker and Emitter // Depend whether we will have to emit into a single file or not either emit only selected file in the project, emit all files into a single file - var emitFilesResult = emitToSingleFile ? getFullTypeCheckChecker().emitFiles(targetSourceFile) : getFullTypeCheckChecker().emitFiles(); + var emitFilesResult = getFullTypeCheckChecker().emitFiles(targetSourceFile); emitOutput.emitOutputStatus = emitFilesResult.emitResultStatus; // Reset writer back to undefined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in getEmitOutput diff --git a/src/services/shims.ts b/src/services/shims.ts index 40ce6c7b8df..c7d293794ca 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -502,7 +502,8 @@ module ts { start: diagnostic.start, length: diagnostic.length, /// TODO: no need for the tolowerCase call - category: DiagnosticCategory[diagnostic.category].toLowerCase() + category: DiagnosticCategory[diagnostic.category].toLowerCase(), + code: diagnostic.code }; } diff --git a/tests/baselines/reference/getEmitOutputExternalModule.baseline b/tests/baselines/reference/getEmitOutputExternalModule.baseline new file mode 100644 index 00000000000..b5e670c5fd7 --- /dev/null +++ b/tests/baselines/reference/getEmitOutputExternalModule.baseline @@ -0,0 +1,9 @@ +EmitOutputStatus : Succeeded +Filename : declSingleFile.js +var x = 5; +var Bar = (function () { + function Bar() { + } + return Bar; +})(); + diff --git a/tests/baselines/reference/getEmitOutputExternalModule2.baseline b/tests/baselines/reference/getEmitOutputExternalModule2.baseline new file mode 100644 index 00000000000..5c03a092257 --- /dev/null +++ b/tests/baselines/reference/getEmitOutputExternalModule2.baseline @@ -0,0 +1,15 @@ +EmitOutputStatus : JSGeneratedWithSemanticErrors +Filename : declSingleFile.js +var x = 5; +var Bar = (function () { + function Bar() { + } + return Bar; +})(); +var x = "world"; +var Bar2 = (function () { + function Bar2() { + } + return Bar2; +})(); + diff --git a/tests/baselines/reference/getEmitOutputSingleFile2.baseline b/tests/baselines/reference/getEmitOutputSingleFile2.baseline index 77fa6b9acdd..2f5dbe3daf8 100644 --- a/tests/baselines/reference/getEmitOutputSingleFile2.baseline +++ b/tests/baselines/reference/getEmitOutputSingleFile2.baseline @@ -5,28 +5,4 @@ exports.bar = "hello world"; Filename : tests/cases/fourslash/inputFile3.d.ts export declare var foo: number; export declare var bar: string; -Filename : declSingleFile.js -var x = 5; -var Bar = (function () { - function Bar() { - } - return Bar; -})(); -var x1 = "hello world"; -var Foo = (function () { - function Foo() { - } - return Foo; -})(); -Filename : declSingleFile.d.ts -declare var x: number; -declare class Bar { - x: string; - y: number; -} -declare var x1: string; -declare class Foo { - x: string; - y: number; -} diff --git a/tests/cases/fourslash/getEmitOutputExternalModule.ts b/tests/cases/fourslash/getEmitOutputExternalModule.ts new file mode 100644 index 00000000000..ef9fb348c57 --- /dev/null +++ b/tests/cases/fourslash/getEmitOutputExternalModule.ts @@ -0,0 +1,19 @@ +/// + +// @BaselineFile: getEmitOutputExternalModule.baseline +// @out: declSingleFile.js + +// @Filename: inputFile1.ts +// @emitThisFile: true +//// var x: number = 5; +//// class Bar { +//// x : string; +//// y : number +//// } + +// @Filename: inputFile2.ts +//// export module M { +//// class C {c} +//// } + +verify.baselineGetEmitOutput(); \ No newline at end of file diff --git a/tests/cases/fourslash/getEmitOutputExternalModule2.ts b/tests/cases/fourslash/getEmitOutputExternalModule2.ts new file mode 100644 index 00000000000..abecc219698 --- /dev/null +++ b/tests/cases/fourslash/getEmitOutputExternalModule2.ts @@ -0,0 +1,26 @@ +/// + +// @BaselineFile: getEmitOutputExternalModule2.baseline +// @out: declSingleFile.js + +// @Filename: inputFile1.ts +//// var x: number = 5; +//// class Bar { +//// x : string; +//// y : number +//// } + +// @Filename: inputFile2.ts +// @emitThisFile: true +//// var x: string = "world"; +//// class Bar2 { +//// x : string; +//// y : number +//// } + +// @Filename: inputFile3.ts +//// export module M { +//// class C {c} +//// } + +verify.baselineGetEmitOutput(); \ No newline at end of file diff --git a/tests/cases/fourslash/navigationItemsOverloads1.ts b/tests/cases/fourslash/navigationItemsOverloads1.ts new file mode 100644 index 00000000000..5f1981fe917 --- /dev/null +++ b/tests/cases/fourslash/navigationItemsOverloads1.ts @@ -0,0 +1,30 @@ +/// + +////function overload(a: string): boolean; +////function overload(b: boolean): boolean; +////function overload(b: number): boolean; +////function overload(f: typeof overload): boolean; +////function overload(x: any, b = (function overload() { return false })): boolean { +//// throw overload; +////} +//// +////interface I { +//// interfaceMethodSignature(a: string): boolean; +//// interfaceMethodSignature(b: boolean): boolean; +//// interfaceMethodSignature(b: number): boolean; +//// interfaceMethodSignature(f: I): boolean; +////} +//// +////class C { +//// methodOverload(a: string): boolean; +//// methodOverload(b: boolean): boolean; +//// methodOverload(b: number): boolean; +//// methodOverload(f: I): boolean; +//// methodOverload(x: any, b = (function overload() { return false })): boolean { +//// throw C; +//// } +////} + +verify.navigationItemsListCount(1, "overload", "exact"); +verify.navigationItemsListCount(1, "interfaceMethodSignature", "exact"); +verify.navigationItemsListCount(1, "methodOverload", "exact"); diff --git a/tests/cases/fourslash/navigationItemsOverloads2.ts b/tests/cases/fourslash/navigationItemsOverloads2.ts new file mode 100644 index 00000000000..c5824b639bc --- /dev/null +++ b/tests/cases/fourslash/navigationItemsOverloads2.ts @@ -0,0 +1,12 @@ +/// + +////interface I { +//// interfaceMethodSignature(a: string): boolean; +//// interfaceMethodSignature(b: number): boolean; +//// interfaceMethodSignature(f: I): boolean; +////} +////interface I { +//// interfaceMethodSignature(b: boolean): boolean; +////} + +verify.navigationItemsListCount(2, "interfaceMethodSignature", "exact"); diff --git a/tests/cases/fourslash/navigationItemsOverloadsBroken1.ts b/tests/cases/fourslash/navigationItemsOverloadsBroken1.ts new file mode 100644 index 00000000000..7b8ebc9181f --- /dev/null +++ b/tests/cases/fourslash/navigationItemsOverloadsBroken1.ts @@ -0,0 +1,29 @@ +/// + +////function overload1(a: string): boolean; +////function overload1(b: boolean): boolean; +////function overload1(b: number): boolean; +//// +////var heyImNotInterruptingAnythingAmI = '?'; +//// +////function overload1(f: typeof overload): boolean; +////function overload1(x: any, b = (function overload() { return false })): boolean { +//// throw overload; +////} + +////function overload2(a: string): boolean; +////function overload2(b: boolean): boolean; +////function overload2(b: number): boolean; +//// +////function iJustRuinEverything(x: any, b = (function overload() { return false })): boolean { +//// throw overload; +////} +//// +////function overload2(f: typeof overload): boolean; +////function overload2(x: any, b = (function overload() { return false })): boolean { +//// throw overload; +////} + +verify.navigationItemsListCount(2, "overload1", "exact"); +verify.navigationItemsListCount(2, "overload2", "exact"); +verify.navigationItemsListCount(4, "overload", "prefix"); \ No newline at end of file diff --git a/tests/cases/fourslash/navigationItemsOverloadsBroken2.ts b/tests/cases/fourslash/navigationItemsOverloadsBroken2.ts new file mode 100644 index 00000000000..c1f6ccae2f5 --- /dev/null +++ b/tests/cases/fourslash/navigationItemsOverloadsBroken2.ts @@ -0,0 +1,23 @@ +/// + +////function overload1(a: string): boolean; +////function overload1(b: boolean): boolean; +////function overload1(x: any, b = (function overload() { return false })): boolean { +//// throw overload1; +////} +////function overload1(b: number): boolean; +////function overload1(f: typeof overload): boolean; + +////function overload2(a: string): boolean; +////function overload2(b: boolean): boolean; +////function overload2(x: any, b = (function overload() { return false })): boolean { +//// function overload2(): boolean; +//// function overload2(x: any): boolean; +//// throw overload2; +////} +////function overload2(b: number): boolean; +////function overload2(f: typeof overload): boolean; + +verify.navigationItemsListCount(1, "overload1", "exact"); +verify.navigationItemsListCount(3, "overload2", "exact"); +verify.navigationItemsListCount(4, "overload", "prefix"); \ No newline at end of file