diff --git a/docs/details/comment-ranges.md b/docs/details/comment-ranges.md index b84bccbe3..42093af6a 100644 --- a/docs/details/comment-ranges.md +++ b/docs/details/comment-ranges.md @@ -12,7 +12,7 @@ Comment ranges are not part of the AST and are generated on request. WARNING: Since comments are generated on demand and not part of the AST, using one after a subsequent manipulation to the source file will throw an error. -### Retreiving +### Retrieving Leading and trailing comment ranges can be retrieved from any node by calling: diff --git a/src/compiler/base/AmbientableNode.ts b/src/compiler/base/AmbientableNode.ts index 0ae1600e2..2d1f5589c 100644 --- a/src/compiler/base/AmbientableNode.ts +++ b/src/compiler/base/AmbientableNode.ts @@ -2,7 +2,7 @@ import { ts, SyntaxKind } from "../../typescript"; import { Constructor } from "../../Constructor"; import * as errors from "../../errors"; import { AmbientableNodeStructure } from "../../structures"; -import { TypeGuards } from "../../utils"; +import { TypeGuards, isNodeAmbientOrInAmbientContext } from "../../utils"; import { callBaseFill } from "../callBaseFill"; import { Node } from "../common"; import { ModifierableNode } from "./ModifierableNode"; @@ -48,20 +48,7 @@ export function AmbientableNode { this._standardWrite(writer, info, () => { - this.global.structurePrinterFactory.forClassDeclaration({ isAmbient: this.sourceFile.isDeclarationFile() }).printTexts(writer, structures); + this.global.structurePrinterFactory.forClassDeclaration({ isAmbient: isNodeAmbientOrInAmbientContext(this) }).printTexts(writer, structures); }); } }); @@ -696,7 +697,7 @@ export function StatementedNode { this._standardWrite(writer, info, () => { - this.global.structurePrinterFactory.forNamespaceDeclaration({ isAmbient: this.sourceFile.isDeclarationFile() }).printTexts(writer, structures); + this.global.structurePrinterFactory.forNamespaceDeclaration({ isAmbient: isNodeAmbientOrInAmbientContext(this) }).printTexts(writer, structures); }); } }); diff --git a/src/tests/compiler/class/classDeclarationTests.ts b/src/tests/compiler/class/classDeclarationTests.ts index a6419cc21..2ed6e2283 100644 --- a/src/tests/compiler/class/classDeclarationTests.ts +++ b/src/tests/compiler/class/classDeclarationTests.ts @@ -3,8 +3,9 @@ import { ClassDeclaration, MethodDeclaration, PropertyDeclaration, GetAccessorDe ConstructorDeclaration, ParameterDeclaration, Scope } from "../../../compiler"; import { PropertyDeclarationStructure, MethodDeclarationStructure, ConstructorDeclarationStructure, ClassDeclarationSpecificStructure, GetAccessorDeclarationStructure, SetAccessorDeclarationStructure } from "../../../structures"; -import { getInfoFromText } from "../testHelpers"; +import { SyntaxKind } from "../../../typescript"; import { TypeGuards } from "../../../utils"; +import { getInfoFromText, getInfoFromTextWithDescendant } from "../testHelpers"; describe(nameof(ClassDeclaration), () => { describe(nameof(c => c.fill), () => { @@ -698,9 +699,9 @@ describe(nameof(ClassDeclaration), () => { describe(nameof(d => d.insertMethods), () => { function doTest(startCode: string, insertIndex: number, structures: MethodDeclarationStructure[], expectedCode: string) { - const {firstChild} = getInfoFromText(startCode); - const result = firstChild.insertMethods(insertIndex, structures); - expect(firstChild.getText()).to.equal(expectedCode); + const {descendant, sourceFile} = getInfoFromTextWithDescendant(startCode, SyntaxKind.ClassDeclaration); + const result = descendant.insertMethods(insertIndex, structures); + expect(sourceFile.getText()).to.equal(expectedCode); expect(result.length).to.equal(structures.length); } @@ -743,6 +744,10 @@ describe(nameof(ClassDeclaration), () => { " console.log('here');\n" + " }\n}"); }); + + it("should write as ambient when inserting into a insert when none exists", () => { + doTest("declare module Ambient { class c {\n} }", 0, [{ name: "method" }], "declare module Ambient { class c {\n method();\n} }"); + }); }); describe(nameof(d => d.insertMethod), () => { diff --git a/src/tests/compiler/statement/statementedNode/classTests.ts b/src/tests/compiler/statement/statementedNode/classTests.ts index 2025e93e9..bd12b8b51 100644 --- a/src/tests/compiler/statement/statementedNode/classTests.ts +++ b/src/tests/compiler/statement/statementedNode/classTests.ts @@ -75,6 +75,17 @@ describe(nameof(StatementedNode), () => { "}\n"; doTest("", 0, [structure], expectedText); }); + + it("should insert an ambient method into a class when inserting a class into an ambient module", () => { + const {sourceFile} = getInfoFromText("declare module Namespace {\n}\n"); + const namespaceDec = sourceFile.getNamespaces()[0]; + namespaceDec.insertClasses(0, [{ + name: "Identifier", + methods: [{ name: "myMethod" }] + }]); + + expect(sourceFile.getFullText()).to.equal("declare module Namespace {\n class Identifier {\n myMethod();\n }\n}\n"); + }); }); describe(nameof(n => n.insertClass), () => { diff --git a/src/tests/compiler/statement/statementedNode/namespaceTests.ts b/src/tests/compiler/statement/statementedNode/namespaceTests.ts index eb2e2727d..781e4ea56 100644 --- a/src/tests/compiler/statement/statementedNode/namespaceTests.ts +++ b/src/tests/compiler/statement/statementedNode/namespaceTests.ts @@ -71,6 +71,21 @@ describe(nameof(StatementedNode), () => { " console.log('here');\n" + "}\n"); }); + + it("should insert an ambient method on a class class when inserting a namespace with a class into an ambient module", () => { + const { sourceFile } = getInfoFromText("declare module Namespace {\n}\n"); + const namespaceDec = sourceFile.getNamespaces()[0]; + namespaceDec.insertNamespaces(0, [{ + name: "Namespace", + classes: [{ + name: "Identifier", + methods: [{ name: "myMethod" }] + }] + }]); + + expect(sourceFile.getFullText()).to.equal("declare module Namespace {\n namespace Namespace {\n class Identifier {\n" + + " myMethod();\n }\n }\n}\n"); + }); }); describe(nameof(n => n.insertNamespace), () => { diff --git a/src/utils/compiler/index.ts b/src/utils/compiler/index.ts index c0dee92e1..7be9b4caf 100644 --- a/src/utils/compiler/index.ts +++ b/src/utils/compiler/index.ts @@ -3,6 +3,7 @@ export * from "./getNodeByNameOrFindFunction"; export * from "./getParentSyntaxList"; export * from "./getSymbolByNameOrFindFunction"; export * from "./getSyntaxKindName"; +export * from "./isNodeAmbientOrInAmbientContext"; export * from "./isStringKind"; export * from "./ModuleUtils"; export * from "./printNode"; diff --git a/src/utils/compiler/isNodeAmbientOrInAmbientContext.ts b/src/utils/compiler/isNodeAmbientOrInAmbientContext.ts new file mode 100644 index 000000000..2db484c91 --- /dev/null +++ b/src/utils/compiler/isNodeAmbientOrInAmbientContext.ts @@ -0,0 +1,20 @@ +import { Node } from "../../compiler"; +import { ts } from "../../typescript"; +import { TypeGuards } from "../TypeGuards"; + +export function isNodeAmbientOrInAmbientContext(node: Node) { + if (checkNodeIsAmbient(node) || node.sourceFile.isDeclarationFile()) + return true; + + for (const ancestor of node.getAncestorsIterator(false)) { + if (checkNodeIsAmbient(ancestor)) + return true; + } + + return false; +} + +function checkNodeIsAmbient(node: Node) { + const isThisAmbient = (node.getCombinedModifierFlags() & ts.ModifierFlags.Ambient) === ts.ModifierFlags.Ambient; + return isThisAmbient || TypeGuards.isInterfaceDeclaration(node) || TypeGuards.isTypeAliasDeclaration(node); +}