From fc81509b6941784192ff5be85713b539a8d5b60a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sun, 5 Nov 2017 22:47:21 -0500 Subject: [PATCH] #27 - Setting JSDoc description. - Getting JSDoc tags. Need to update the documentation for this before releasing. Still a lot of JSDoc stuff not implemented. --- src/compiler/base/BodiedNode.ts | 2 +- src/compiler/base/BodyableNode.ts | 2 +- src/compiler/base/DocumentationableNode.ts | 2 +- .../base/name/DeclarationNamedNode.ts | 8 ++ src/compiler/common/Node.ts | 8 +- src/compiler/doc.ts | 9 ++ src/compiler/doc/JSDoc.ts | 53 ++++++++- src/compiler/doc/JSDocAugmentsTag.ts | 9 ++ src/compiler/doc/JSDocClassTag.ts | 8 ++ src/compiler/doc/JSDocParameterTag.ts | 10 ++ src/compiler/doc/JSDocPropertyTag.ts | 10 ++ src/compiler/doc/JSDocReturnTag.ts | 9 ++ src/compiler/doc/JSDocTag.ts | 28 +++++ src/compiler/doc/JSDocTypeTag.ts | 9 ++ src/compiler/doc/JSDocTypedefTag.ts | 9 ++ src/compiler/doc/JSDocUnknownTag.ts | 8 ++ src/compiler/doc/base.ts | 1 + src/compiler/doc/base/JSDocPropertyLikeTag.ts | 15 +++ src/factories/nodeToWrapperMappings.ts | 9 +- src/manipulation/code/getIndentedText.ts | 2 +- .../getNewReplacementSourceFile.ts | 2 +- .../insertIntoCreatableSyntaxList.ts | 2 +- .../insertion/insertIntoParent.ts | 2 +- .../insertion/insertIntoParentTextRange.ts | 2 +- .../insertion/insertSyntaxList.ts | 2 +- src/manipulation/removal/unwrapNode.ts | 2 +- src/manipulation/replaction.ts | 1 + .../replaceTextPossiblyCreatingChildNodes.ts | 30 +++++ src/tests/compiler/doc/jsDocTests.ts | 89 +++++++++++++++ src/tests/compiler/doc/jsdocTests.ts | 60 ++++++++++ src/utils/TypeGuards.ts | 104 ++++++++++++++++++ 31 files changed, 489 insertions(+), 18 deletions(-) create mode 100644 src/compiler/doc/JSDocAugmentsTag.ts create mode 100644 src/compiler/doc/JSDocClassTag.ts create mode 100644 src/compiler/doc/JSDocParameterTag.ts create mode 100644 src/compiler/doc/JSDocPropertyTag.ts create mode 100644 src/compiler/doc/JSDocReturnTag.ts create mode 100644 src/compiler/doc/JSDocTag.ts create mode 100644 src/compiler/doc/JSDocTypeTag.ts create mode 100644 src/compiler/doc/JSDocTypedefTag.ts create mode 100644 src/compiler/doc/JSDocUnknownTag.ts create mode 100644 src/compiler/doc/base.ts create mode 100644 src/compiler/doc/base/JSDocPropertyLikeTag.ts rename src/manipulation/{insertion => }/getNewReplacementSourceFile.ts (91%) create mode 100644 src/manipulation/replaction/replaceTextPossiblyCreatingChildNodes.ts create mode 100644 src/tests/compiler/doc/jsDocTests.ts diff --git a/src/compiler/base/BodiedNode.ts b/src/compiler/base/BodiedNode.ts index 0f48076a0..5df7e3bd2 100644 --- a/src/compiler/base/BodiedNode.ts +++ b/src/compiler/base/BodiedNode.ts @@ -16,7 +16,7 @@ export interface BodiedNode { getBody(): Node; /** * Sets the body text. - * @param writerFunction - Function for using a writer to write out the body text. + * @param writerFunction - Write the text using the provided writer. */ setBodyText(writerFunction: (writer: CodeBlockWriter) => void): this; /** diff --git a/src/compiler/base/BodyableNode.ts b/src/compiler/base/BodyableNode.ts index 197db354b..03e7ff995 100644 --- a/src/compiler/base/BodyableNode.ts +++ b/src/compiler/base/BodyableNode.ts @@ -20,7 +20,7 @@ export interface BodyableNode { getBody(): Node | undefined; /** * Sets the body text. A body is required to do this operation. - * @param writerFunction - Function for using a writer to write out the body text. + * @param writerFunction - Write the text using the provided writer. */ setBodyText(writerFunction: (writer: CodeBlockWriter) => void): this; /** diff --git a/src/compiler/base/DocumentationableNode.ts b/src/compiler/base/DocumentationableNode.ts index 6c8e383a8..856c6e582 100644 --- a/src/compiler/base/DocumentationableNode.ts +++ b/src/compiler/base/DocumentationableNode.ts @@ -50,7 +50,7 @@ export function DocumentationableNode (n.compilerNode.comment || "").trim()); + const texts = docCommentNodes.map(n => (n.getComment() || "").trim()); return texts.filter(t => t.length > 0).join(this.global.manipulationSettings.getNewLineKind()); } diff --git a/src/compiler/base/name/DeclarationNamedNode.ts b/src/compiler/base/name/DeclarationNamedNode.ts index 6c42f405a..912d65c0c 100644 --- a/src/compiler/base/name/DeclarationNamedNode.ts +++ b/src/compiler/base/name/DeclarationNamedNode.ts @@ -21,6 +21,10 @@ export interface DeclarationNamedNode { * Gets the name. */ getName(): string | undefined; + /** + * Gets the name or throws if it doens't exist. + */ + getNameOrThrow(): string; /** * Renames the name. * @param text - Text to set as the name. @@ -52,6 +56,10 @@ export function DeclarationNamedNode implements Disposable { } /** - * Gets the position of the start of the line that this node is on. + * Gets the position of the start of the line that this node starts on. */ getStartLinePos() { const sourceFileText = this.sourceFile.getFullText(); - const startPos = this.getStart(); - - return getPreviousMatchingPos(sourceFileText, startPos, char => char === "\n"); + return getPreviousMatchingPos(sourceFileText, this.getStart(), char => char === "\n"); } /** @@ -705,7 +703,7 @@ export class Node implements Disposable { * Replaces the text of the current node with new text. * * This will dispose the current node and return a new node that can be asserted or type guarded to the correct type. - * @param writerFunction - Writer function to replace the text with. + * @param writerFunction - Write the text using the provided writer. * @returns The new node. */ replaceWithText(writerFunction: (writer: CodeBlockWriter) => void): Node; diff --git a/src/compiler/doc.ts b/src/compiler/doc.ts index e137681b0..0a942fdac 100644 --- a/src/compiler/doc.ts +++ b/src/compiler/doc.ts @@ -1 +1,10 @@ export * from "./doc/JSDoc"; +export * from "./doc/JSDocTag"; +export * from "./doc/JSDocUnknownTag"; +export * from "./doc/JSDocAugmentsTag"; +export * from "./doc/JSDocClassTag"; +export * from "./doc/JSDocReturnTag"; +export * from "./doc/JSDocTypeTag"; +export * from "./doc/JSDocTypedefTag"; +export * from "./doc/JSDocPropertyTag"; +export * from "./doc/JSDocParameterTag"; diff --git a/src/compiler/doc/JSDoc.ts b/src/compiler/doc/JSDoc.ts index 0370e2170..a78ef0394 100644 --- a/src/compiler/doc/JSDoc.ts +++ b/src/compiler/doc/JSDoc.ts @@ -1,11 +1,60 @@ import * as ts from "typescript"; -import {removeChildren} from "./../../manipulation"; +import CodeBlockWriter from "code-block-writer"; +import {removeChildren, replaceTextPossiblyCreatingChildNodes} from "./../../manipulation"; +import {getPreviousMatchingPos} from "./../../manipulation/textSeek"; +import {getTextFromStringOrWriter} from "./../../utils"; import {Node} from "./../common"; /** - * A js doc node. + * JS doc node. */ export class JSDoc extends Node { + /** + * Gets the tags of the JSDoc. + */ + getTags(): Node[] { + const tags = this.compilerNode.tags; + if (tags == null) + return []; + return tags.map(t => this.global.compilerFactory.getNodeFromCompilerNode(t, this.sourceFile)) as Node[]; + } + + /** + * Gets the comment. + */ + getComment() { + return this.compilerNode.comment; + } + + /** + * Sets the comment. + * @param writerFunction - Write the text using the provided writer. + */ + setComment(writerFunction: (writer: CodeBlockWriter) => void): this; + /** + * Sets the comment. + * @param text - Text of the comment. + */ + setComment(text: string): this; + setComment(textOrWriterFunction: string | ((writer: CodeBlockWriter) => void)) { + const tags = this.getTags(); + const startEditPos = this.getStart() + 3; + const endEditPos = tags.length > 0 ? getPreviousMatchingPos(this.sourceFile.getFullText(), tags[0].getStart(), c => c === "*") - 1 : this.getEnd() - 2; + const indentationText = this.getIndentationText(); + const newLineKind = this.global.manipulationSettings.getNewLineKind(); + const text = getTextFromStringOrWriter(this.global.manipulationSettings, textOrWriterFunction); + const newText = newLineKind + text.split(/\r?\n/).map(l => `${indentationText} * ${l}`).join(newLineKind) + newLineKind + indentationText + " "; + + replaceTextPossiblyCreatingChildNodes({ + parent: this, + newText, + replacePos: startEditPos, + replacingLength: endEditPos - startEditPos + }); + + return this; + } + /** * Removes this JSDoc. */ diff --git a/src/compiler/doc/JSDocAugmentsTag.ts b/src/compiler/doc/JSDocAugmentsTag.ts new file mode 100644 index 000000000..a0b738867 --- /dev/null +++ b/src/compiler/doc/JSDocAugmentsTag.ts @@ -0,0 +1,9 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; + +/** + * JS doc augments tag node. + */ +export class JSDocAugmentsTag extends JSDocTag { + // todo: helper methods +} diff --git a/src/compiler/doc/JSDocClassTag.ts b/src/compiler/doc/JSDocClassTag.ts new file mode 100644 index 000000000..c8a94b46f --- /dev/null +++ b/src/compiler/doc/JSDocClassTag.ts @@ -0,0 +1,8 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; + +/** + * JS doc class tag node. + */ +export class JSDocClassTag extends JSDocTag { +} diff --git a/src/compiler/doc/JSDocParameterTag.ts b/src/compiler/doc/JSDocParameterTag.ts new file mode 100644 index 000000000..727b21014 --- /dev/null +++ b/src/compiler/doc/JSDocParameterTag.ts @@ -0,0 +1,10 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; +import {JSDocPropertyLikeTag} from "./base"; + +export const JSDocParameterTagBase = JSDocPropertyLikeTag(JSDocTag); +/** + * JS doc parameter tag node. + */ +export class JSDocParameterTag extends JSDocParameterTagBase { +} diff --git a/src/compiler/doc/JSDocPropertyTag.ts b/src/compiler/doc/JSDocPropertyTag.ts new file mode 100644 index 000000000..895e4d336 --- /dev/null +++ b/src/compiler/doc/JSDocPropertyTag.ts @@ -0,0 +1,10 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; +import {JSDocPropertyLikeTag} from "./base"; + +export const JSDocPropertyTagBase = JSDocPropertyLikeTag(JSDocTag); +/** + * JS doc property tag node. + */ +export class JSDocPropertyTag extends JSDocPropertyTagBase { +} diff --git a/src/compiler/doc/JSDocReturnTag.ts b/src/compiler/doc/JSDocReturnTag.ts new file mode 100644 index 000000000..a6d2d298d --- /dev/null +++ b/src/compiler/doc/JSDocReturnTag.ts @@ -0,0 +1,9 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; + +/** + * JS doc return tag node. + */ +export class JSDocReturnTag extends JSDocTag { + // todo: helper methods +} diff --git a/src/compiler/doc/JSDocTag.ts b/src/compiler/doc/JSDocTag.ts new file mode 100644 index 000000000..dfede00cb --- /dev/null +++ b/src/compiler/doc/JSDocTag.ts @@ -0,0 +1,28 @@ +import * as ts from "typescript"; +import {Node, Identifier} from "./../common"; + +/** + * JS doc tag node. + */ +export class JSDocTag extends Node { + /** + * Gets the at token. + */ + getAtToken() { + return this.global.compilerFactory.getNodeFromCompilerNode(this.compilerNode.atToken, this.sourceFile) as Node; + } + + /** + * Gets the tag name identifier. + */ + getTagNameIdentifier() { + return this.global.compilerFactory.getNodeFromCompilerNode(this.compilerNode.tagName, this.sourceFile) as Identifier; + } + + /** + * Gets the tag's comment. + */ + getComment() { + return this.compilerNode.comment; + } +} diff --git a/src/compiler/doc/JSDocTypeTag.ts b/src/compiler/doc/JSDocTypeTag.ts new file mode 100644 index 000000000..a48f447df --- /dev/null +++ b/src/compiler/doc/JSDocTypeTag.ts @@ -0,0 +1,9 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; + +/** + * JS doc type tag node. + */ +export class JSDocTypeTag extends JSDocTag { + // todo: helper methods +} diff --git a/src/compiler/doc/JSDocTypedefTag.ts b/src/compiler/doc/JSDocTypedefTag.ts new file mode 100644 index 000000000..2e367979b --- /dev/null +++ b/src/compiler/doc/JSDocTypedefTag.ts @@ -0,0 +1,9 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; + +/** + * JS doc type def tag node. + */ +export class JSDocTypedefTag extends JSDocTag { + // todo: helper methods +} diff --git a/src/compiler/doc/JSDocUnknownTag.ts b/src/compiler/doc/JSDocUnknownTag.ts new file mode 100644 index 000000000..2e0992f27 --- /dev/null +++ b/src/compiler/doc/JSDocUnknownTag.ts @@ -0,0 +1,8 @@ +import * as ts from "typescript"; +import {JSDocTag} from "./JSDocTag"; + +/** + * JS doc unknown tag node. + */ +export class JSDocUnknownTag extends JSDocTag { +} diff --git a/src/compiler/doc/base.ts b/src/compiler/doc/base.ts new file mode 100644 index 000000000..af67c11df --- /dev/null +++ b/src/compiler/doc/base.ts @@ -0,0 +1 @@ +export * from "./base/JSDocPropertyLikeTag"; diff --git a/src/compiler/doc/base/JSDocPropertyLikeTag.ts b/src/compiler/doc/base/JSDocPropertyLikeTag.ts new file mode 100644 index 000000000..2d0eb791b --- /dev/null +++ b/src/compiler/doc/base/JSDocPropertyLikeTag.ts @@ -0,0 +1,15 @@ +import * as ts from "typescript"; +import {Constructor} from "./../../../Constructor"; +import {Node} from "./../../common"; + +export type JSDocPropertyLikeTagExtensionType = Node /*& ModifierableNode*/; + +export interface JSDocPropertyLikeTag { + // todo: methods +} + +export function JSDocPropertyLikeTag>(Base: T): Constructor & T { + return class extends Base implements JSDocPropertyLikeTag { + // todo: methods + }; +} diff --git a/src/factories/nodeToWrapperMappings.ts b/src/factories/nodeToWrapperMappings.ts index 42485346e..96f71734f 100644 --- a/src/factories/nodeToWrapperMappings.ts +++ b/src/factories/nodeToWrapperMappings.ts @@ -44,5 +44,12 @@ export const nodeToWrapperMappings: { [key: number]: any } = { [ts.SyntaxKind.VariableStatement]: compiler.VariableStatement, [ts.SyntaxKind.JSDocComment]: compiler.JSDoc, [ts.SyntaxKind.FirstTypeNode]: compiler.TypeNode, - [ts.SyntaxKind.LastTypeNode]: compiler.TypeNode + [ts.SyntaxKind.LastTypeNode]: compiler.TypeNode, + [ts.SyntaxKind.JSDocTag]: compiler.JSDocUnknownTag, + [ts.SyntaxKind.JSDocAugmentsTag]: compiler.JSDocAugmentsTag, + [ts.SyntaxKind.JSDocClassTag]: compiler.JSDocClassTag, + [ts.SyntaxKind.JSDocReturnTag]: compiler.JSDocReturnTag, + [ts.SyntaxKind.JSDocTypeTag]: compiler.JSDocTypeTag, + [ts.SyntaxKind.JSDocTypedefTag]: compiler.JSDocTypedefTag, + [ts.SyntaxKind.JSDocPropertyTag]: compiler.JSDocPropertyTag }; diff --git a/src/manipulation/code/getIndentedText.ts b/src/manipulation/code/getIndentedText.ts index cc0737426..0f301e660 100644 --- a/src/manipulation/code/getIndentedText.ts +++ b/src/manipulation/code/getIndentedText.ts @@ -11,7 +11,7 @@ export interface GetIndentedTextOptions { export function getIndentedText(opts: GetIndentedTextOptions) { const {textOrWriterFunction, manipulationSettings, indentationText} = opts; const newLineKind = manipulationSettings.getNewLineKind(); - const originalText = getTextFromStringOrWriter(manipulationSettings, textOrWriterFunction) || ""; + const originalText = getTextFromStringOrWriter(manipulationSettings, textOrWriterFunction); if (originalText.length > 0) return originalText.split(/\r?\n/).map(t => t.length > 0 ? indentationText + t : t).join(newLineKind); return originalText; diff --git a/src/manipulation/insertion/getNewReplacementSourceFile.ts b/src/manipulation/getNewReplacementSourceFile.ts similarity index 91% rename from src/manipulation/insertion/getNewReplacementSourceFile.ts rename to src/manipulation/getNewReplacementSourceFile.ts index 1990b462d..a2efb18e5 100644 --- a/src/manipulation/insertion/getNewReplacementSourceFile.ts +++ b/src/manipulation/getNewReplacementSourceFile.ts @@ -1,4 +1,4 @@ -import {Node, SourceFile} from "./../../compiler"; +import {Node, SourceFile} from "./../compiler"; export interface GetNewReplacementSourceFileOptions { insertPos: number; diff --git a/src/manipulation/insertion/insertIntoCreatableSyntaxList.ts b/src/manipulation/insertion/insertIntoCreatableSyntaxList.ts index c62b27b12..f8bb989e0 100644 --- a/src/manipulation/insertion/insertIntoCreatableSyntaxList.ts +++ b/src/manipulation/insertion/insertIntoCreatableSyntaxList.ts @@ -1,6 +1,6 @@ import {Node} from "./../../compiler"; import {replaceTreeCreatingSyntaxList, replaceTreeWithChildIndex} from "./../tree"; -import {getNewReplacementSourceFile} from "./getNewReplacementSourceFile"; +import {getNewReplacementSourceFile} from "./../getNewReplacementSourceFile"; import {insertSyntaxList} from "./insertSyntaxList"; import {insertIntoParent} from "./insertIntoParent"; diff --git a/src/manipulation/insertion/insertIntoParent.ts b/src/manipulation/insertion/insertIntoParent.ts index 6ce77b1c2..af72f3a9f 100644 --- a/src/manipulation/insertion/insertIntoParent.ts +++ b/src/manipulation/insertion/insertIntoParent.ts @@ -1,6 +1,6 @@ import {Node} from "./../../compiler"; import {replaceTreeWithChildIndex} from "./../tree"; -import {getNewReplacementSourceFile} from "./getNewReplacementSourceFile"; +import {getNewReplacementSourceFile} from "./../getNewReplacementSourceFile"; export interface InsertIntoParentOptions { insertPos: number; diff --git a/src/manipulation/insertion/insertIntoParentTextRange.ts b/src/manipulation/insertion/insertIntoParentTextRange.ts index b40a03396..3b072af15 100644 --- a/src/manipulation/insertion/insertIntoParentTextRange.ts +++ b/src/manipulation/insertion/insertIntoParentTextRange.ts @@ -1,6 +1,6 @@ import {Node} from "./../../compiler"; import {replaceTreeWithRange} from "./../tree"; -import {getNewReplacementSourceFile} from "./getNewReplacementSourceFile"; +import {getNewReplacementSourceFile} from "./../getNewReplacementSourceFile"; export interface InsertIntoParentTextRangeOptions { insertPos: number; diff --git a/src/manipulation/insertion/insertSyntaxList.ts b/src/manipulation/insertion/insertSyntaxList.ts index 40dd9b16b..942ecff2c 100644 --- a/src/manipulation/insertion/insertSyntaxList.ts +++ b/src/manipulation/insertion/insertSyntaxList.ts @@ -1,6 +1,6 @@ import {Node} from "./../../compiler"; import {replaceTreeCreatingSyntaxList} from "./../tree"; -import {getNewReplacementSourceFile} from "./getNewReplacementSourceFile"; +import {getNewReplacementSourceFile} from "./../getNewReplacementSourceFile"; export interface InsertSyntaxListOptions { insertPos: number; diff --git a/src/manipulation/removal/unwrapNode.ts b/src/manipulation/removal/unwrapNode.ts index af39e590a..9d76d6d57 100644 --- a/src/manipulation/removal/unwrapNode.ts +++ b/src/manipulation/removal/unwrapNode.ts @@ -2,7 +2,7 @@ import {Node} from "./../../compiler"; import {isStringNode} from "./../../utils"; import {replaceTreeUnwrappingNode} from "./../tree"; -import {getNewReplacementSourceFile} from "./../insertion/getNewReplacementSourceFile"; +import {getNewReplacementSourceFile} from "./../getNewReplacementSourceFile"; export function unwrapNode(node: Node) { const tempSourceFile = getNewReplacementSourceFile({ diff --git a/src/manipulation/replaction.ts b/src/manipulation/replaction.ts index b3d85a0d8..6a20ab6dd 100644 --- a/src/manipulation/replaction.ts +++ b/src/manipulation/replaction.ts @@ -1,2 +1,3 @@ export * from "./replaction/replaceNodeText"; export * from "./replaction/replaceSourceFileTextForFormatting"; +export * from "./replaction/replaceTextPossiblyCreatingChildNodes"; diff --git a/src/manipulation/replaction/replaceTextPossiblyCreatingChildNodes.ts b/src/manipulation/replaction/replaceTextPossiblyCreatingChildNodes.ts new file mode 100644 index 000000000..292a51bbd --- /dev/null +++ b/src/manipulation/replaction/replaceTextPossiblyCreatingChildNodes.ts @@ -0,0 +1,30 @@ +import {Node} from "./../../compiler"; +import {replaceTreeWithRange} from "./../tree"; +import {getNewReplacementSourceFile} from "./../getNewReplacementSourceFile"; + +export interface ReplaceTextPossiblyCreatingChildNodesOptions { + replacePos: number; + replacingLength: number; + newText: string; + parent: Node; +} + +/** + * Replaces a node text while possibly creating new child nodes. + */ +export function replaceTextPossiblyCreatingChildNodes(opts: ReplaceTextPossiblyCreatingChildNodesOptions) { + const {replacePos, replacingLength, newText, parent} = opts; + const tempSourceFile = getNewReplacementSourceFile({ + sourceFile: parent.getSourceFile(), + insertPos: replacePos, + replacingLength, + newText + }); + + replaceTreeWithRange({ + parent, + replacementSourceFile: tempSourceFile, + start: replacePos, + end: replacePos + newText.length + }); +} diff --git a/src/tests/compiler/doc/jsDocTests.ts b/src/tests/compiler/doc/jsDocTests.ts new file mode 100644 index 000000000..5187f2c71 --- /dev/null +++ b/src/tests/compiler/doc/jsDocTests.ts @@ -0,0 +1,89 @@ +import {expect} from "chai"; +import {JSDoc} from "./../../../compiler"; +import {getInfoFromText} from "./../testHelpers"; + +describe(nameof(JSDoc), () => { + describe(nameof(d => d.remove), () => { + function doTest(text: string, index: number, jsDocIndex: number, expectedText: string) { + const {sourceFile} = getInfoFromText(text); + sourceFile.getFunctions()[index].getDocumentationCommentNodes()[jsDocIndex].remove(); + expect(sourceFile.getFullText()).to.equal(expectedText); + } + + it("should remove the js doc", () => { + doTest("enum I {}\n\n/** Test */\nfunction func() {}", 0, 0, "enum I {}\n\nfunction func() {}"); + }); + + it("should remove the js doc when first", () => { + doTest("enum I {}\n\n/** first */\n/** second */\nfunction func() {}", 0, 0, "enum I {}\n\n/** second */\nfunction func() {}"); + }); + + it("should remove the js doc when in the middle", () => { + doTest("enum I {}\n\n/** first */\n/** second */\n/** third */\nfunction func() {}", 0, 1, "enum I {}\n\n/** first */\n/** third */\nfunction func() {}"); + }); + + it("should remove the js doc when last", () => { + doTest("enum I {}\n\n/** first */\n/** second */\nfunction func() {}", 0, 1, "enum I {}\n\n/** first */\nfunction func() {}"); + }); + }); + + describe(nameof(d => d.getComment), () => { + function doTest(text: string, expectedComment: string | undefined) { + const {sourceFile} = getInfoFromText(text); + const comment = sourceFile.getFunctions()[0].getDocumentationCommentNodes()[0].getComment(); + expect(comment).to.equal(expectedComment); + } + + it("should get the comment when it exists", () => { + doTest("/**\n * Description\n */function identifier() {}", "Description"); + }); + + it("should be undefined when it doesn't exist", () => { + doTest("/**\n *\n */function identifier() {}", undefined); + }); + }); + + describe(nameof(d => d.setComment), () => { + function doTest(text: string, comment: string, expectedText: string) { + const {sourceFile} = getInfoFromText(text); + sourceFile.getFunctions()[0].getDocumentationCommentNodes()[0].setComment(comment); + expect(sourceFile.getFullText()).to.equal(expectedText); + } + + it("should set a new comment with one line", () => { + doTest("/**\n * Description\n */function identifier() {}", "New Text", "/**\n * New Text\n */function identifier() {}"); + }); + + it("should set a new comment with multiple lines", () => { + doTest("/**\n * Description\n */function identifier() {}", "One\nTwo\r\nThree", "/**\n * One\n * Two\n * Three\n */function identifier() {}"); + }); + + it("should set a new comment when originally all on the same line", () => { + doTest("/** Description */function identifier() {}", "New", "/**\n * New\n */function identifier() {}"); + }); + + it("should set a new comment without affecting the tags", () => { + doTest("/**\n * Description\n * @param - Something */function identifier() {}", "New", "/**\n * New\n * @param - Something */function identifier() {}"); + }); + + it("should set a new comment without affecting the tags when the first tag has some space before it", () => { + doTest("/**\n * Description\n * @param - Something */function identifier() {}", "New", "/**\n * New\n * @param - Something */function identifier() {}"); + }); + }); + + describe(nameof(d => d.getTags), () => { + function doTest(text: string, expectedTags: string[]) { + const {sourceFile} = getInfoFromText(text); + const tags = sourceFile.getFunctions()[0].getDocumentationCommentNodes()[0].getTags(); + expect(tags.map(t => t.getText())).to.deep.equal(expectedTags); + } + + it("should return an empty array when no tags exist", () => { + doTest("/**\n * Description\n */function identifier() {}", []); + }); + + it("should return the tags when they exist", () => { + doTest("/**\n * Description\n * @param test - Test\n * @returns A value\n */function identifier() {}", ["@param test ", "@returns "]); + }); + }); +}); diff --git a/src/tests/compiler/doc/jsdocTests.ts b/src/tests/compiler/doc/jsdocTests.ts index a9a085b6b..5187f2c71 100644 --- a/src/tests/compiler/doc/jsdocTests.ts +++ b/src/tests/compiler/doc/jsdocTests.ts @@ -26,4 +26,64 @@ describe(nameof(JSDoc), () => { doTest("enum I {}\n\n/** first */\n/** second */\nfunction func() {}", 0, 1, "enum I {}\n\n/** first */\nfunction func() {}"); }); }); + + describe(nameof(d => d.getComment), () => { + function doTest(text: string, expectedComment: string | undefined) { + const {sourceFile} = getInfoFromText(text); + const comment = sourceFile.getFunctions()[0].getDocumentationCommentNodes()[0].getComment(); + expect(comment).to.equal(expectedComment); + } + + it("should get the comment when it exists", () => { + doTest("/**\n * Description\n */function identifier() {}", "Description"); + }); + + it("should be undefined when it doesn't exist", () => { + doTest("/**\n *\n */function identifier() {}", undefined); + }); + }); + + describe(nameof(d => d.setComment), () => { + function doTest(text: string, comment: string, expectedText: string) { + const {sourceFile} = getInfoFromText(text); + sourceFile.getFunctions()[0].getDocumentationCommentNodes()[0].setComment(comment); + expect(sourceFile.getFullText()).to.equal(expectedText); + } + + it("should set a new comment with one line", () => { + doTest("/**\n * Description\n */function identifier() {}", "New Text", "/**\n * New Text\n */function identifier() {}"); + }); + + it("should set a new comment with multiple lines", () => { + doTest("/**\n * Description\n */function identifier() {}", "One\nTwo\r\nThree", "/**\n * One\n * Two\n * Three\n */function identifier() {}"); + }); + + it("should set a new comment when originally all on the same line", () => { + doTest("/** Description */function identifier() {}", "New", "/**\n * New\n */function identifier() {}"); + }); + + it("should set a new comment without affecting the tags", () => { + doTest("/**\n * Description\n * @param - Something */function identifier() {}", "New", "/**\n * New\n * @param - Something */function identifier() {}"); + }); + + it("should set a new comment without affecting the tags when the first tag has some space before it", () => { + doTest("/**\n * Description\n * @param - Something */function identifier() {}", "New", "/**\n * New\n * @param - Something */function identifier() {}"); + }); + }); + + describe(nameof(d => d.getTags), () => { + function doTest(text: string, expectedTags: string[]) { + const {sourceFile} = getInfoFromText(text); + const tags = sourceFile.getFunctions()[0].getDocumentationCommentNodes()[0].getTags(); + expect(tags.map(t => t.getText())).to.deep.equal(expectedTags); + } + + it("should return an empty array when no tags exist", () => { + doTest("/**\n * Description\n */function identifier() {}", []); + }); + + it("should return the tags when they exist", () => { + doTest("/**\n * Description\n * @param test - Test\n * @returns A value\n */function identifier() {}", ["@param test ", "@returns "]); + }); + }); }); diff --git a/src/utils/TypeGuards.ts b/src/utils/TypeGuards.ts index 3d6eafd1c..870615313 100644 --- a/src/utils/TypeGuards.ts +++ b/src/utils/TypeGuards.ts @@ -547,6 +547,110 @@ export class TypeGuards { } } + /** + * Gets if the node is a JSDocAugmentsTag. + * @param node - Node to check. + */ + static isJSDocAugmentsTag(node: compiler.Node): node is compiler.JSDocAugmentsTag { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocAugmentsTag: + return true; + default: + return false; + } + } + + /** + * Gets if the node is a JSDocClassTag. + * @param node - Node to check. + */ + static isJSDocClassTag(node: compiler.Node): node is compiler.JSDocClassTag { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocClassTag: + return true; + default: + return false; + } + } + + /** + * Gets if the node is a JSDocPropertyLikeTag. + * @param node - Node to check. + */ + static isJSDocPropertyLikeTag(node: compiler.Node): node is compiler.JSDocPropertyLikeTag & compiler.Node { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocPropertyTag: + return true; + default: + return false; + } + } + + /** + * Gets if the node is a JSDocPropertyTag. + * @param node - Node to check. + */ + static isJSDocPropertyTag(node: compiler.Node): node is compiler.JSDocPropertyTag { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocPropertyTag: + return true; + default: + return false; + } + } + + /** + * Gets if the node is a JSDocReturnTag. + * @param node - Node to check. + */ + static isJSDocReturnTag(node: compiler.Node): node is compiler.JSDocReturnTag { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocReturnTag: + return true; + default: + return false; + } + } + + /** + * Gets if the node is a JSDocTypeTag. + * @param node - Node to check. + */ + static isJSDocTypeTag(node: compiler.Node): node is compiler.JSDocTypeTag { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocTypeTag: + return true; + default: + return false; + } + } + + /** + * Gets if the node is a JSDocTypedefTag. + * @param node - Node to check. + */ + static isJSDocTypedefTag(node: compiler.Node): node is compiler.JSDocTypedefTag { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocTypedefTag: + return true; + default: + return false; + } + } + + /** + * Gets if the node is a JSDocUnknownTag. + * @param node - Node to check. + */ + static isJSDocUnknownTag(node: compiler.Node): node is compiler.JSDocUnknownTag { + switch (node.getKind()) { + case ts.SyntaxKind.JSDocTag: + return true; + default: + return false; + } + } + /** * Gets if the node is a MethodDeclaration. * @param node - Node to check.