mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 08:33:30 +01:00
* fix(ExceptText): don't display import("d..-types/v10"). in return type
* Squashed 'packages/api-extractor-model/' content from commit 39ecb196c
git-subtree-dir: packages/api-extractor-model
git-subtree-split: 39ecb196ca210bdf84ba6c9cadb1bb93571849d7
* Squashed 'packages/api-extractor/' content from commit 341ad6c51
git-subtree-dir: packages/api-extractor
git-subtree-split: 341ad6c51b01656d4f73b74ad4bdb3095f9262c4
* feat(api-extractor): add api-extractor and -model
* fix: package.json docs script
* fix(SourcLink): use <> instead of function syntax
* fix: make packages private
* fix: rest params showing in docs, added labels
* fix: missed two files
* fix: cpy-cli & pnpm-lock
* fix: increase icon size
* fix: icon size again
684 lines
20 KiB
TypeScript
684 lines
20 KiB
TypeScript
/* eslint-disable promise/prefer-await-to-callbacks */
|
|
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
|
// See LICENSE in the project root for license information.
|
|
|
|
import { InternalError, Sort } from '@rushstack/node-core-library';
|
|
import * as ts from 'typescript';
|
|
import { IndentedWriter } from '../generators/IndentedWriter.js';
|
|
|
|
interface IWriteModifiedTextOptions {
|
|
indentDocCommentState: IndentDocCommentState;
|
|
separatorOverride: string | undefined;
|
|
writer: IndentedWriter;
|
|
}
|
|
|
|
enum IndentDocCommentState {
|
|
/**
|
|
* `indentDocComment` was not requested for this subtree.
|
|
*/
|
|
Inactive = 0,
|
|
/**
|
|
* `indentDocComment` was requested and we are looking for the opening `/` `*`
|
|
*/
|
|
AwaitingOpenDelimiter = 1,
|
|
/**
|
|
* `indentDocComment` was requested and we are looking for the closing `*` `/`
|
|
*/
|
|
AwaitingCloseDelimiter = 2,
|
|
/**
|
|
* `indentDocComment` was requested and we have finished indenting the comment.
|
|
*/
|
|
Done = 3,
|
|
}
|
|
|
|
/**
|
|
* Choices for SpanModification.indentDocComment.
|
|
*/
|
|
export enum IndentDocCommentScope {
|
|
/**
|
|
* Do not detect and indent comments.
|
|
*/
|
|
None = 0,
|
|
|
|
/**
|
|
* Look for one doc comment in the {@link Span.prefix} text only.
|
|
*/
|
|
PrefixOnly = 1,
|
|
|
|
/**
|
|
* Look for one doc comment potentially distributed across the Span and its children.
|
|
*/
|
|
SpanAndChildren = 2,
|
|
}
|
|
|
|
/**
|
|
* Specifies various transformations that will be performed by Span.getModifiedText().
|
|
*/
|
|
export class SpanModification {
|
|
/**
|
|
* If true, all of the child spans will be omitted from the Span.getModifiedText() output.
|
|
*
|
|
* @remarks
|
|
* Also, the modify() operation will not recurse into these spans.
|
|
*/
|
|
public omitChildren: boolean = false;
|
|
|
|
/**
|
|
* If true, then the Span.separator will be removed from the Span.getModifiedText() output.
|
|
*/
|
|
public omitSeparatorAfter: boolean = false;
|
|
|
|
/**
|
|
* If true, then Span.getModifiedText() will sort the immediate children according to their Span.sortKey
|
|
* property. The separators will also be fixed up to ensure correct indentation. If the Span.sortKey is undefined
|
|
* for some items, those items will not be moved, i.e. their array indexes will be unchanged.
|
|
*/
|
|
public sortChildren: boolean = false;
|
|
|
|
/**
|
|
* Used if the parent span has Span.sortChildren=true.
|
|
*/
|
|
public sortKey: string | undefined;
|
|
|
|
/**
|
|
* Optionally configures getModifiedText() to search for a "/*" doc comment and indent it.
|
|
* At most one comment is detected.
|
|
*
|
|
* @remarks
|
|
* The indentation can be applied to the `Span.modifier.prefix` only, or it can be applied to the
|
|
* full subtree of nodes (as needed for `ts.SyntaxKind.JSDocComment` trees). However the enabled
|
|
* scopes must not overlap.
|
|
*
|
|
* This feature is enabled selectively because (1) we do not want to accidentally match `/*` appearing
|
|
* in a string literal or other expression that is not a comment, and (2) parsing comments is relatively
|
|
* expensive.
|
|
*/
|
|
public indentDocComment: IndentDocCommentScope = IndentDocCommentScope.None;
|
|
|
|
private readonly _span: Span;
|
|
|
|
private _prefix: string | undefined;
|
|
|
|
private _suffix: string | undefined;
|
|
|
|
public constructor(span: Span) {
|
|
this._span = span;
|
|
this.reset();
|
|
}
|
|
|
|
/**
|
|
* Allows the Span.prefix text to be changed.
|
|
*/
|
|
public get prefix(): string {
|
|
return this._prefix ?? this._span.prefix;
|
|
}
|
|
|
|
public set prefix(value: string) {
|
|
this._prefix = value;
|
|
}
|
|
|
|
/**
|
|
* Allows the Span.suffix text to be changed.
|
|
*/
|
|
public get suffix(): string {
|
|
return this._suffix ?? this._span.suffix;
|
|
}
|
|
|
|
public set suffix(value: string) {
|
|
this._suffix = value;
|
|
}
|
|
|
|
/**
|
|
* Reverts any modifications made to this object.
|
|
*/
|
|
public reset(): void {
|
|
this.omitChildren = false;
|
|
this.omitSeparatorAfter = false;
|
|
this.sortChildren = false;
|
|
this.sortKey = undefined;
|
|
this._prefix = undefined;
|
|
this._suffix = undefined;
|
|
if (this._span.kind === ts.SyntaxKind.JSDocComment) {
|
|
this.indentDocComment = IndentDocCommentScope.SpanAndChildren;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Effectively deletes the Span from the tree, by skipping its children, skipping its separator,
|
|
* and setting its prefix/suffix to the empty string.
|
|
*/
|
|
public skipAll(): void {
|
|
this.prefix = '';
|
|
this.suffix = '';
|
|
this.omitChildren = true;
|
|
this.omitSeparatorAfter = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The Span class provides a simple way to rewrite TypeScript source files
|
|
* based on simple syntax transformations, i.e. without having to process deeper aspects
|
|
* of the underlying grammar. An example transformation might be deleting JSDoc comments
|
|
* from a source file.
|
|
*
|
|
* @remarks
|
|
* TypeScript's abstract syntax tree (AST) is represented using Node objects.
|
|
* The Node text ignores its surrounding whitespace, and does not have an ordering guarantee.
|
|
* For example, a JSDocComment node can be a child of a FunctionDeclaration node, even though
|
|
* the actual comment precedes the function in the input stream.
|
|
*
|
|
* The Span class is a wrapper for a single Node, that provides access to every character
|
|
* in the input stream, such that Span.getText() will exactly reproduce the corresponding
|
|
* full Node.getText() output.
|
|
*
|
|
* A Span is comprised of these parts, which appear in sequential order:
|
|
* - A prefix
|
|
* - A collection of child spans
|
|
* - A suffix
|
|
* - A separator (e.g. whitespace between this span and the next item in the tree)
|
|
*
|
|
* These parts can be modified via Span.modification. The modification is applied by
|
|
* calling Span.getModifiedText().
|
|
*/
|
|
export class Span {
|
|
public readonly node: ts.Node;
|
|
|
|
// To improve performance, substrings are not allocated until actually needed
|
|
public readonly startIndex: number;
|
|
|
|
public readonly endIndex: number;
|
|
|
|
public readonly children: Span[];
|
|
|
|
public readonly modification: SpanModification;
|
|
|
|
private readonly _parent: Span | undefined;
|
|
|
|
private readonly _previousSibling: Span | undefined;
|
|
|
|
private readonly _nextSibling: Span | undefined;
|
|
|
|
private readonly _separatorStartIndex: number;
|
|
|
|
private readonly _separatorEndIndex: number;
|
|
|
|
public constructor(node: ts.Node) {
|
|
this.node = node;
|
|
this.startIndex = node.kind === ts.SyntaxKind.SourceFile ? node.getFullStart() : node.getStart();
|
|
this.endIndex = node.end;
|
|
this._separatorStartIndex = 0;
|
|
this._separatorEndIndex = 0;
|
|
this.children = [];
|
|
this.modification = new SpanModification(this);
|
|
|
|
let previousChildSpan: Span | undefined;
|
|
|
|
for (const childNode of this.node.getChildren() || []) {
|
|
const childSpan: Span = new Span(childNode);
|
|
// @ts-expect-error assigning private readonly properties on creation only
|
|
childSpan._parent = this;
|
|
// @ts-expect-error assigning private readonly properties on creation only
|
|
childSpan._previousSibling = previousChildSpan;
|
|
|
|
if (previousChildSpan) {
|
|
// @ts-expect-error assigning private readonly properties on creation only
|
|
previousChildSpan._nextSibling = childSpan;
|
|
}
|
|
|
|
this.children.push(childSpan);
|
|
|
|
// Normalize the bounds so that a child is never outside its parent
|
|
if (childSpan.startIndex < this.startIndex) {
|
|
this.startIndex = childSpan.startIndex;
|
|
}
|
|
|
|
if (childSpan.endIndex > this.endIndex) {
|
|
// This has never been observed empirically, but here's how we would handle it
|
|
this.endIndex = childSpan.endIndex;
|
|
throw new InternalError('Unexpected AST case');
|
|
}
|
|
|
|
if (previousChildSpan && previousChildSpan.endIndex < childSpan.startIndex) {
|
|
// There is some leftover text after previous child -- assign it as the separator for
|
|
// the preceding span. If the preceding span has no suffix, then assign it to the
|
|
// deepest preceding span with no suffix. This heuristic simplifies the most
|
|
// common transformations, and otherwise it can be fished out using getLastInnerSeparator().
|
|
let separatorRecipient: Span = previousChildSpan;
|
|
while (separatorRecipient.children.length > 0) {
|
|
const lastChild: Span = separatorRecipient.children[separatorRecipient.children.length - 1]!;
|
|
if (lastChild.endIndex !== separatorRecipient.endIndex) {
|
|
// There is a suffix, so we cannot push the separator any further down, or else
|
|
// it would get printed before this suffix.
|
|
break;
|
|
}
|
|
|
|
separatorRecipient = lastChild;
|
|
}
|
|
|
|
// @ts-expect-error assigning private readonly properties on creation only
|
|
separatorRecipient._separatorStartIndex = previousChildSpan.endIndex;
|
|
// @ts-expect-error assigning private readonly properties on creation only
|
|
separatorRecipient._separatorEndIndex = childSpan.startIndex;
|
|
}
|
|
|
|
previousChildSpan = childSpan;
|
|
}
|
|
}
|
|
|
|
public get kind(): ts.SyntaxKind {
|
|
return this.node.kind;
|
|
}
|
|
|
|
/**
|
|
* The parent Span, if any.
|
|
* NOTE: This will be undefined for a root Span, even though the corresponding Node
|
|
* may have a parent in the AST.
|
|
*/
|
|
public get parent(): Span | undefined {
|
|
return this._parent;
|
|
}
|
|
|
|
/**
|
|
* If the current object is this.parent.children[i], then previousSibling corresponds
|
|
* to this.parent.children[i-1] if it exists.
|
|
* NOTE: This will be undefined for a root Span, even though the corresponding Node
|
|
* may have a previous sibling in the AST.
|
|
*/
|
|
public get previousSibling(): Span | undefined {
|
|
return this._previousSibling;
|
|
}
|
|
|
|
/**
|
|
* If the current object is this.parent.children[i], then previousSibling corresponds
|
|
* to this.parent.children[i+1] if it exists.
|
|
* NOTE: This will be undefined for a root Span, even though the corresponding Node
|
|
* may have a previous sibling in the AST.
|
|
*/
|
|
public get nextSibling(): Span | undefined {
|
|
return this._nextSibling;
|
|
}
|
|
|
|
/**
|
|
* The text associated with the underlying Node, up to its first child.
|
|
*/
|
|
public get prefix(): string {
|
|
if (this.children.length) {
|
|
// Everything up to the first child
|
|
return this._getSubstring(this.startIndex, this.children[0]!.startIndex);
|
|
} else {
|
|
return this._getSubstring(this.startIndex, this.endIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The text associated with the underlying Node, after its last child.
|
|
* If there are no children, this is always an empty string.
|
|
*/
|
|
public get suffix(): string {
|
|
if (this.children.length) {
|
|
// Everything after the last child
|
|
return this._getSubstring(this.children[this.children.length - 1]!.endIndex, this.endIndex);
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whitespace that appeared after this node, and before the "next" node in the tree.
|
|
* Here we mean "next" according to an inorder traversal, not necessarily a sibling.
|
|
*/
|
|
public get separator(): string {
|
|
return this._getSubstring(this._separatorStartIndex, this._separatorEndIndex);
|
|
}
|
|
|
|
/**
|
|
* Returns the separator of this Span, or else recursively calls getLastInnerSeparator()
|
|
* on the last child.
|
|
*/
|
|
public getLastInnerSeparator(): string {
|
|
if (this.separator) {
|
|
return this.separator;
|
|
}
|
|
|
|
if (this.children.length > 0) {
|
|
return this.children[this.children.length - 1]!.getLastInnerSeparator();
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Returns the first parent node with the specified SyntaxKind, or undefined if there is no match.
|
|
*/
|
|
public findFirstParent(kindToMatch: ts.SyntaxKind): Span | undefined {
|
|
let current: Span | undefined = this;
|
|
|
|
while (current) {
|
|
if (current.kind === kindToMatch) {
|
|
return current;
|
|
}
|
|
|
|
current = current.parent;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Recursively invokes the callback on this Span and all its children. The callback
|
|
* can make changes to Span.modification for each node.
|
|
*/
|
|
public forEach(callback: (span: Span) => void): void {
|
|
// eslint-disable-next-line n/callback-return, n/no-callback-literal
|
|
callback(this);
|
|
for (const child of this.children) {
|
|
// eslint-disable-next-line unicorn/no-array-for-each
|
|
child.forEach(callback);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the original unmodified text represented by this Span.
|
|
*/
|
|
public getText(): string {
|
|
let result = '';
|
|
result += this.prefix;
|
|
|
|
for (const child of this.children) {
|
|
result += child.getText();
|
|
}
|
|
|
|
result += this.suffix;
|
|
result += this.separator;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns the text represented by this Span, after applying all requested modifications.
|
|
*/
|
|
public getModifiedText(): string {
|
|
const writer: IndentedWriter = new IndentedWriter();
|
|
writer.trimLeadingSpaces = true;
|
|
|
|
this._writeModifiedText({
|
|
writer,
|
|
separatorOverride: undefined,
|
|
indentDocCommentState: IndentDocCommentState.Inactive,
|
|
});
|
|
|
|
return writer.getText();
|
|
}
|
|
|
|
public writeModifiedText(output: IndentedWriter): void {
|
|
this._writeModifiedText({
|
|
writer: output,
|
|
separatorOverride: undefined,
|
|
indentDocCommentState: IndentDocCommentState.Inactive,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns a diagnostic dump of the tree, showing the prefix/suffix/separator for
|
|
* each node.
|
|
*/
|
|
public getDump(indent: string = ''): string {
|
|
let result: string = indent + ts.SyntaxKind[this.node.kind] + ': ';
|
|
|
|
if (this.prefix) {
|
|
result += ' pre=[' + this._getTrimmed(this.prefix) + ']';
|
|
}
|
|
|
|
if (this.suffix) {
|
|
result += ' suf=[' + this._getTrimmed(this.suffix) + ']';
|
|
}
|
|
|
|
if (this.separator) {
|
|
result += ' sep=[' + this._getTrimmed(this.separator) + ']';
|
|
}
|
|
|
|
result += '\n';
|
|
|
|
for (const child of this.children) {
|
|
result += child.getDump(indent + ' ');
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns a diagnostic dump of the tree, showing the SpanModification settings for each nodde.
|
|
*/
|
|
public getModifiedDump(indent: string = ''): string {
|
|
let result: string = indent + ts.SyntaxKind[this.node.kind] + ': ';
|
|
|
|
if (this.prefix) {
|
|
result += ' pre=[' + this._getTrimmed(this.modification.prefix) + ']';
|
|
}
|
|
|
|
if (this.suffix) {
|
|
result += ' suf=[' + this._getTrimmed(this.modification.suffix) + ']';
|
|
}
|
|
|
|
if (this.separator) {
|
|
result += ' sep=[' + this._getTrimmed(this.separator) + ']';
|
|
}
|
|
|
|
if (this.modification.indentDocComment !== IndentDocCommentScope.None) {
|
|
result += ' indentDocComment=' + IndentDocCommentScope[this.modification.indentDocComment];
|
|
}
|
|
|
|
if (this.modification.omitChildren) {
|
|
result += ' omitChildren';
|
|
}
|
|
|
|
if (this.modification.omitSeparatorAfter) {
|
|
result += ' omitSeparatorAfter';
|
|
}
|
|
|
|
if (this.modification.sortChildren) {
|
|
result += ' sortChildren';
|
|
}
|
|
|
|
if (this.modification.sortKey !== undefined) {
|
|
result += ` sortKey="${this.modification.sortKey}"`;
|
|
}
|
|
|
|
result += '\n';
|
|
|
|
if (this.modification.omitChildren) {
|
|
result += `${indent} (${this.children.length} children)\n`;
|
|
} else {
|
|
for (const child of this.children) {
|
|
result += child.getModifiedDump(indent + ' ');
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Recursive implementation of `getModifiedText()` and `writeModifiedText()`.
|
|
*/
|
|
private _writeModifiedText(options: IWriteModifiedTextOptions): void {
|
|
// Apply indentation based on "{" and "}"
|
|
if (this.prefix === '{') {
|
|
options.writer.increaseIndent();
|
|
} else if (this.prefix === '}') {
|
|
options.writer.decreaseIndent();
|
|
}
|
|
|
|
if (this.modification.indentDocComment !== IndentDocCommentScope.None) {
|
|
this._beginIndentDocComment(options);
|
|
}
|
|
|
|
this._write(this.modification.prefix, options);
|
|
|
|
if (this.modification.indentDocComment === IndentDocCommentScope.PrefixOnly) {
|
|
this._endIndentDocComment(options);
|
|
}
|
|
|
|
let sortedSubset: Span[] | undefined;
|
|
|
|
if (!this.modification.omitChildren && this.modification.sortChildren) {
|
|
// We will only sort the items with a sortKey
|
|
const filtered: Span[] = this.children.filter((x) => x.modification.sortKey !== undefined);
|
|
|
|
// Is there at least one of them?
|
|
if (filtered.length > 1) {
|
|
sortedSubset = filtered;
|
|
}
|
|
}
|
|
|
|
if (sortedSubset) {
|
|
// This is the complicated special case that sorts an arbitrary subset of the child nodes,
|
|
// preserving the surrounding nodes.
|
|
|
|
const sortedSubsetCount: number = sortedSubset.length;
|
|
// Remember the separator for the first and last ones
|
|
const firstSeparator: string = sortedSubset[0]!.getLastInnerSeparator();
|
|
const lastSeparator: string = sortedSubset[sortedSubsetCount - 1]!.getLastInnerSeparator();
|
|
|
|
Sort.sortBy(sortedSubset, (x) => x.modification.sortKey);
|
|
|
|
const childOptions: IWriteModifiedTextOptions = { ...options };
|
|
|
|
let sortedSubsetIndex = 0;
|
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
for (let index = 0; index < this.children.length; ++index) {
|
|
let current: Span;
|
|
|
|
// Is this an item that we sorted?
|
|
if (this.children[index]!.modification.sortKey === undefined) {
|
|
// No, take the next item from the original array
|
|
current = this.children[index]!;
|
|
childOptions.separatorOverride = undefined;
|
|
} else {
|
|
// Yes, take the next item from the sortedSubset
|
|
current = sortedSubset[sortedSubsetIndex++]!;
|
|
|
|
if (sortedSubsetIndex < sortedSubsetCount) {
|
|
childOptions.separatorOverride = firstSeparator;
|
|
} else {
|
|
childOptions.separatorOverride = lastSeparator;
|
|
}
|
|
}
|
|
|
|
current._writeModifiedText(childOptions);
|
|
}
|
|
} else {
|
|
// This is the normal case that does not need to sort children
|
|
const childrenLength: number = this.children.length;
|
|
|
|
if (!this.modification.omitChildren) {
|
|
if (options.separatorOverride === undefined) {
|
|
// The normal simple case
|
|
for (const child of this.children) {
|
|
child._writeModifiedText(options);
|
|
}
|
|
} else {
|
|
// Special case where the separatorOverride is passed down to the "last inner separator" span
|
|
for (let index = 0; index < childrenLength; ++index) {
|
|
const child: Span = this.children[index]!;
|
|
|
|
if (
|
|
// Only the last child inherits the separatorOverride, because only it can contain
|
|
// the "last inner separator" span
|
|
index < childrenLength - 1 ||
|
|
// If this.separator is specified, then we will write separatorOverride below, so don't pass it along
|
|
this.separator
|
|
) {
|
|
const childOptions: IWriteModifiedTextOptions = { ...options };
|
|
childOptions.separatorOverride = undefined;
|
|
child._writeModifiedText(childOptions);
|
|
} else {
|
|
child._writeModifiedText(options);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this._write(this.modification.suffix, options);
|
|
|
|
if (options.separatorOverride !== undefined) {
|
|
if (this.separator || childrenLength === 0) {
|
|
this._write(options.separatorOverride, options);
|
|
}
|
|
} else if (!this.modification.omitSeparatorAfter) {
|
|
this._write(this.separator, options);
|
|
}
|
|
}
|
|
|
|
if (this.modification.indentDocComment === IndentDocCommentScope.SpanAndChildren) {
|
|
this._endIndentDocComment(options);
|
|
}
|
|
}
|
|
|
|
private _beginIndentDocComment(options: IWriteModifiedTextOptions): void {
|
|
if (options.indentDocCommentState !== IndentDocCommentState.Inactive) {
|
|
throw new InternalError('indentDocComment cannot be nested');
|
|
}
|
|
|
|
options.indentDocCommentState = IndentDocCommentState.AwaitingOpenDelimiter;
|
|
}
|
|
|
|
private _endIndentDocComment(options: IWriteModifiedTextOptions): void {
|
|
if (options.indentDocCommentState === IndentDocCommentState.AwaitingCloseDelimiter) {
|
|
throw new InternalError('missing "*/" delimiter for comment block');
|
|
}
|
|
|
|
options.indentDocCommentState = IndentDocCommentState.Inactive;
|
|
}
|
|
|
|
/**
|
|
* Writes one chunk of `text` to the `options.writer`, applying the `indentDocComment` rewriting.
|
|
*/
|
|
private _write(text: string, options: IWriteModifiedTextOptions): void {
|
|
let parsedText: string = text;
|
|
|
|
if (options.indentDocCommentState === IndentDocCommentState.AwaitingOpenDelimiter) {
|
|
let index: number = parsedText.indexOf('/*');
|
|
if (index >= 0) {
|
|
index += '/*'.length;
|
|
options.writer.write(parsedText.slice(0, Math.max(0, index)));
|
|
parsedText = parsedText.slice(Math.max(0, index));
|
|
options.indentDocCommentState = IndentDocCommentState.AwaitingCloseDelimiter;
|
|
|
|
options.writer.increaseIndent(' ');
|
|
}
|
|
}
|
|
|
|
if (options.indentDocCommentState === IndentDocCommentState.AwaitingCloseDelimiter) {
|
|
let index: number = parsedText.indexOf('*/');
|
|
if (index >= 0) {
|
|
index += '*/'.length;
|
|
options.writer.write(parsedText.slice(0, Math.max(0, index)));
|
|
parsedText = parsedText.slice(Math.max(0, index));
|
|
options.indentDocCommentState = IndentDocCommentState.Done;
|
|
|
|
options.writer.decreaseIndent();
|
|
}
|
|
}
|
|
|
|
options.writer.write(parsedText);
|
|
}
|
|
|
|
private _getTrimmed(text: string): string {
|
|
const trimmed: string = text.replaceAll(/\r?\n/g, '\\n');
|
|
|
|
if (trimmed.length > 100) {
|
|
return trimmed.slice(0, 97) + '...';
|
|
}
|
|
|
|
return trimmed;
|
|
}
|
|
|
|
private _getSubstring(startIndex: number, endIndex: number): string {
|
|
if (startIndex === endIndex) {
|
|
return '';
|
|
}
|
|
|
|
return this.node.getSourceFile().text.slice(startIndex, endIndex);
|
|
}
|
|
}
|