From 2ad455bf3f4b589fa2c8c00280f5e5723e3960dc Mon Sep 17 00:00:00 2001 From: Kir_Antipov Date: Tue, 31 Jan 2023 13:22:14 +0000 Subject: [PATCH] Made class that represents TypeScript comments --- src/utils/typescript/typescript-comment.ts | 244 +++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 src/utils/typescript/typescript-comment.ts diff --git a/src/utils/typescript/typescript-comment.ts b/src/utils/typescript/typescript-comment.ts new file mode 100644 index 0000000..1b03a5d --- /dev/null +++ b/src/utils/typescript/typescript-comment.ts @@ -0,0 +1,244 @@ +import { splitLines } from "@/utils/string-utils"; +import { getIndentation, getNewline, TypeScriptFormattingOptions, UNIX_NEWLINE } from "./typescript-formatting-options"; +import { TypeScriptNode } from "./typescript-node"; + +/** + * Represents a TypeScript comment. + */ +export class TypeScriptComment implements TypeScriptNode { + /** + * The text of the comment. + */ + private readonly _text: string; + + /** + * Constructs a new {@link TypeScriptComment} instance with the given text. + * + * @param text - The text of the comment. + */ + private constructor(text: string) { + this._text = text; + } + + /** + * Gets the text of the comment. + */ + get text(): string { + return this._text; + } + + /** + * Determines whether the comment is a single-line comment. + */ + get isSingleline(): boolean { + return isSinglelineComment(this._text); + } + + /** + * Determines whether the comment is a directive comment. + */ + get isDirective(): boolean { + return isDirectiveComment(this._text); + } + + /** + * Determines whether the comment is a multi-line comment. + */ + get isMultiline(): boolean { + return isMultilineComment(this._text); + } + + /** + * Determines whether the comment is a TSDoc comment. + */ + get isTSDoc(): boolean { + return isTSDocComment(this._text); + } + + /** + * Creates a {@link TypeScriptComment} from the given text or text lines and optional template. + * + * @param text - The text or text lines to create the comment from. + * @param template - The template to use when formatting the comment, if any. + * + * @returns A new {@link TypeScriptComment} created from the given text. + */ + static create(text: string | Iterable, template?: TypeScriptCommentTemplate): TypeScriptComment { + const lines = typeof text === "string" ? splitLines(text) : [...text]; + template ||= lines.length > 1 ? MULTILINE_TEMPLATE : SINGLELINE_TEMPLATE; + + const commentedLines = lines.map(x => template.prefix + x.trim()); + if (typeof template.startDelimiter === "string") { + commentedLines.unshift(template.startDelimiter); + } + if (typeof template.endDelimiter === "string") { + commentedLines.push(template.endDelimiter); + } + + const commentText = commentedLines.join(UNIX_NEWLINE); + return new TypeScriptComment(commentText); + } + + /** + * Creates a single-line {@link TypeScriptComment} from the given text or text lines. + * + * @param text - The text or text lines to create the comment from. + * + * @returns A new single-line {@link TypeScriptComment} created from the given text. + */ + static createSingleline(text: string | Iterable): TypeScriptComment { + return TypeScriptComment.create(text, SINGLELINE_TEMPLATE); + } + + /** + * Creates a directive {@link TypeScriptComment} from the given text or text lines. + * + * @param text - The text or text lines to create the comment from. + * + * @returns A new directive {@link TypeScriptComment} created from the given text. + */ + static createDirective(text: string | Iterable): TypeScriptComment { + return TypeScriptComment.create(text, DIRECTIVE_TEMPLATE); + } + + /** + * Creates a multi-line {@link TypeScriptComment} from the given text or text lines. + * + * @param text - The text or text lines to create the comment from. + * + * @returns A new multi-line {@link TypeScriptComment} created from the given text. + */ + static createMultiline(text: string | Iterable): TypeScriptComment { + return TypeScriptComment.create(text, MULTILINE_TEMPLATE); + } + + /** + * Creates a TSDoc {@link TypeScriptComment} from the given text or text lines. + * + * @param text - The text or text lines to create the comment from. + * + * @returns A new TSDoc {@link TypeScriptComment} created from the given text. + */ + static createTSDoc(text: string | Iterable): TypeScriptComment { + return TypeScriptComment.create(text, TSDOC_TEMPLATE); + } + + /** + * Parses the given text as a comment. + * + * @param text - The text to parse. + * + * @returns A {@link TypeScriptComment} created from the given text. + */ + static parse(text: string): TypeScriptComment { + return isComment(text) ? new TypeScriptComment(text) : TypeScriptComment.create(text); + } + + /** + * @inheritdoc + */ + format(options?: TypeScriptFormattingOptions): string { + const indent = getIndentation(options); + const newline = getNewline(options); + + const lines = splitLines(this._text); + const comment = lines.map(x => indent + x).join(newline); + + return comment; + } +} + +/** + * Determines whether the given text represents a single-line comment. + * + * @param text - The text to check. + * + * @returns `true` if the text represents a single-line comment; otherwise, `false`. + */ +export function isSinglelineComment(text: string): boolean { + return /^\s*\/\//.test(text); +} + +/** + * Determines whether the given text represents a directive comment. + * + * @param text - The text to check. + * + * @returns `true` if the text represents a directive comment; otherwise, `false`. + */ +export function isDirectiveComment(text: string): boolean { + return /^\s*\/\/\//.test(text); +} + +/** + * Determines whether the given text represents a multi-line comment. + * + * @param text - The text to check. + * + * @returns `true` if the text represents a multi-line comment; otherwise, `false`. + */ +export function isMultilineComment(text: string): boolean { + return /^\s*\/\*/.test(text); +} + +/** + * Determines whether the given text represents a TSDoc comment. + * + * @param text - The text to check. + * + * @returns `true` if the text represents a TSDoc comment; otherwise, `false`. + */ +export function isTSDocComment(text: string): boolean { + return /^\s*\/\*\*/.test(text); +} + +/** + * Determines whether the given text represents a comment. + * + * @param text - The text to check. + * + * @returns `true` if the text represents a comment; otherwise, `false`. + */ +export function isComment(text: string): boolean { + return /^\s*\/[/*]/.test(text); +} + +/** + * Represents a template for formatting TypeScript comments. + */ +export interface TypeScriptCommentTemplate { + /** + * The starting delimiter of the comment, if any. + */ + startDelimiter?: string; + + /** + * The prefix used to start each line of the comment. + */ + prefix: string; + + /** + * The ending delimiter of the comment, if any. + */ + endDelimiter?: string; +} + +/** + * A pre-defined {@link TypeScriptCommentTemplate} for single-line comments. + */ +export const SINGLELINE_TEMPLATE: TypeScriptCommentTemplate = { prefix: "// " }; + +/** + * A pre-defined {@link TypeScriptCommentTemplate} for directive comments. + */ +export const DIRECTIVE_TEMPLATE: TypeScriptCommentTemplate = { prefix: "/// " }; + +/** + * A pre-defined {@link TypeScriptCommentTemplate} for multi-line comments. + */ +export const MULTILINE_TEMPLATE: TypeScriptCommentTemplate = { startDelimiter: "/*", prefix: " * ", endDelimiter: " */" }; + +/** + * A pre-defined {@link TypeScriptCommentTemplate} for TSDoc comments. + */ +export const TSDOC_TEMPLATE: TypeScriptCommentTemplate = { startDelimiter: "/**", prefix: " * ", endDelimiter: " */" };