mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge existing JSDoc comments (#27978)
* Correct indentation, using correct (I hope) indentation code Note that part of the code, in formatting.ts, is cloned but should be extracted to a function instead. * Remove some possibly-superfluous code But I see 4 failures with whitespace, so perhaps not. * Restrict indentation change to avoid breaking baselines The indentation code is very complex so I'm just going to avoid breaking our single-line tests for now, plus add a simple jsdoc test to show that multiline jsdoc indentation isn't destroyed in the common case. * Switched over to construction for @return/@type Still doesn't merge correctly though * Add @return tags to emitter * Merge multiple jsdocs (not for @param yet) * Merge multiple jsdoc for parameters too * Emit more jsdoc tags Not all of them; I got cold feet since I'll have to write tests for them. I'll do that tomorrow. * Many fixes to JSDoc emit And single tests (at least) for all tags * Cleanup in textChanges.ts * Cleanup in formatting.ts (Plus a little more in textChanges.ts) * Cleanup in inferFromUsage.ts * Fix minor omissions * Separate merged top-level JSDoc comments with \n instead of space. * Don't delete intrusive non-jsdoc comments * Cleanup from PR comments 1. Refactor emit code into smaller functions. 2. Preceding-whitespace utility is slightly easier to use. 3. Better casts and types in inferFromUsage make it easier to read. * Fix bogus newline * Use @andy-ms' cleanup annotateJSDocParameters
This commit is contained in:
committed by
GitHub
parent
e46c846ee6
commit
fe2a33fcbc
+187
-2
@@ -862,7 +862,34 @@ namespace ts {
|
||||
case SyntaxKind.EnumMember:
|
||||
return emitEnumMember(<EnumMember>node);
|
||||
|
||||
// JSDoc nodes (ignored)
|
||||
// JSDoc nodes (only used in codefixes currently)
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag);
|
||||
case SyntaxKind.JSDocReturnTag:
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
case SyntaxKind.JSDocThisTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return emitJSDocSimpleTypedTag(node as JSDocTypeTag);
|
||||
case SyntaxKind.JSDocAugmentsTag:
|
||||
return emitJSDocAugmentsTag(node as JSDocAugmentsTag);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return emitJSDocTemplateTag(node as JSDocTemplateTag);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
return emitJSDocTypedefTag(node as JSDocTypedefTag);
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
return emitJSDocCallbackTag(node as JSDocCallbackTag);
|
||||
case SyntaxKind.JSDocSignature:
|
||||
return emitJSDocSignature(node as JSDocSignature);
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
return emitJSDocTypeLiteral(node as JSDocTypeLiteral);
|
||||
case SyntaxKind.JSDocClassTag:
|
||||
case SyntaxKind.JSDocTag:
|
||||
return emitJSDocSimpleTag(node as JSDocTag);
|
||||
|
||||
case SyntaxKind.JSDocComment:
|
||||
return emitJSDoc(node as JSDoc);
|
||||
|
||||
// Transformation nodes (ignored)
|
||||
}
|
||||
|
||||
@@ -2584,6 +2611,154 @@ namespace ts {
|
||||
emitInitializer(node.initializer, node.name.end, node);
|
||||
}
|
||||
|
||||
//
|
||||
// JSDoc
|
||||
//
|
||||
function emitJSDoc(node: JSDoc) {
|
||||
write("/**");
|
||||
if (node.comment) {
|
||||
const lines = node.comment.split(/\r\n?|\n/g);
|
||||
for (const line of lines) {
|
||||
writeLine();
|
||||
writeSpace();
|
||||
writePunctuation("*");
|
||||
writeSpace();
|
||||
write(line);
|
||||
}
|
||||
}
|
||||
if (node.tags) {
|
||||
if (node.tags.length === 1 && node.tags[0].kind === SyntaxKind.JSDocTypeTag && !node.comment) {
|
||||
writeSpace();
|
||||
emit(node.tags[0]);
|
||||
}
|
||||
else {
|
||||
emitList(node, node.tags, ListFormat.JSDocComment);
|
||||
}
|
||||
}
|
||||
writeSpace();
|
||||
write("*/");
|
||||
}
|
||||
|
||||
function emitJSDocSimpleTypedTag(tag: JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocReturnTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
emitJSDocTypeExpression(tag.typeExpression);
|
||||
emitJSDocComment(tag.comment);
|
||||
}
|
||||
|
||||
function emitJSDocAugmentsTag(tag: JSDocAugmentsTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
writeSpace();
|
||||
writePunctuation("{");
|
||||
emit(tag.class);
|
||||
writePunctuation("}");
|
||||
emitJSDocComment(tag.comment);
|
||||
}
|
||||
|
||||
function emitJSDocTemplateTag(tag: JSDocTemplateTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
emitJSDocTypeExpression(tag.constraint);
|
||||
writeSpace();
|
||||
emitList(tag, tag.typeParameters, ListFormat.CommaListElements);
|
||||
emitJSDocComment(tag.comment);
|
||||
}
|
||||
|
||||
function emitJSDocTypedefTag(tag: JSDocTypedefTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
if (tag.typeExpression) {
|
||||
if (tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
|
||||
emitJSDocTypeExpression(tag.typeExpression);
|
||||
}
|
||||
else {
|
||||
writeSpace();
|
||||
writePunctuation("{");
|
||||
write("Object");
|
||||
if (tag.typeExpression.isArrayType) {
|
||||
writePunctuation("[");
|
||||
writePunctuation("]");
|
||||
}
|
||||
writePunctuation("}");
|
||||
}
|
||||
}
|
||||
if (tag.fullName) {
|
||||
writeSpace();
|
||||
emit(tag.fullName);
|
||||
}
|
||||
emitJSDocComment(tag.comment);
|
||||
if (tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeLiteral) {
|
||||
emitJSDocTypeLiteral(tag.typeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
function emitJSDocCallbackTag(tag: JSDocCallbackTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
if (tag.name) {
|
||||
writeSpace();
|
||||
emit(tag.name);
|
||||
}
|
||||
emitJSDocComment(tag.comment);
|
||||
emitJSDocSignature(tag.typeExpression);
|
||||
}
|
||||
|
||||
function emitJSDocSimpleTag(tag: JSDocTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
emitJSDocComment(tag.comment);
|
||||
}
|
||||
|
||||
function emitJSDocTypeLiteral(lit: JSDocTypeLiteral) {
|
||||
emitList(lit, createNodeArray(lit.jsDocPropertyTags), ListFormat.JSDocComment);
|
||||
}
|
||||
|
||||
function emitJSDocSignature(sig: JSDocSignature) {
|
||||
if (sig.typeParameters) {
|
||||
emitList(sig, createNodeArray(sig.typeParameters), ListFormat.JSDocComment);
|
||||
}
|
||||
if (sig.parameters) {
|
||||
emitList(sig, createNodeArray(sig.parameters), ListFormat.JSDocComment);
|
||||
}
|
||||
if (sig.type) {
|
||||
writeLine();
|
||||
writeSpace();
|
||||
writePunctuation("*");
|
||||
writeSpace();
|
||||
emit(sig.type);
|
||||
}
|
||||
}
|
||||
|
||||
function emitJSDocPropertyLikeTag(param: JSDocPropertyLikeTag) {
|
||||
emitJSDocTagName(param.tagName);
|
||||
emitJSDocTypeExpression(param.typeExpression);
|
||||
writeSpace();
|
||||
if (param.isBracketed) {
|
||||
writePunctuation("[");
|
||||
}
|
||||
emit(param.name);
|
||||
if (param.isBracketed) {
|
||||
writePunctuation("]");
|
||||
}
|
||||
emitJSDocComment(param.comment);
|
||||
}
|
||||
|
||||
function emitJSDocTagName(tagName: Identifier) {
|
||||
writePunctuation("@");
|
||||
emit(tagName);
|
||||
}
|
||||
|
||||
function emitJSDocComment(comment: string | undefined) {
|
||||
if (comment) {
|
||||
writeSpace();
|
||||
write(comment);
|
||||
}
|
||||
}
|
||||
|
||||
function emitJSDocTypeExpression(typeExpression: JSDocTypeExpression | undefined) {
|
||||
if (typeExpression) {
|
||||
writeSpace();
|
||||
writePunctuation("{");
|
||||
emit(typeExpression.type);
|
||||
writePunctuation("}");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Top-level nodes
|
||||
//
|
||||
@@ -2875,6 +3050,11 @@ namespace ts {
|
||||
writeSpace();
|
||||
writePunctuation("|");
|
||||
break;
|
||||
case ListFormat.AsteriskDelimited:
|
||||
writeSpace();
|
||||
writePunctuation("*");
|
||||
writeSpace();
|
||||
break;
|
||||
case ListFormat.AmpersandDelimited:
|
||||
writeSpace();
|
||||
writePunctuation("&");
|
||||
@@ -2944,7 +3124,12 @@ namespace ts {
|
||||
const child = children![start + i];
|
||||
|
||||
// Write the delimiter if this is not the first node.
|
||||
if (previousSibling) {
|
||||
if (format & ListFormat.AsteriskDelimited) {
|
||||
// always write JSDoc in the format "\n *"
|
||||
writeLine();
|
||||
writeDelimiter(format);
|
||||
}
|
||||
else if (previousSibling) {
|
||||
// i.e
|
||||
// function commentedParameters(
|
||||
// /* Parameter a */
|
||||
|
||||
@@ -2170,6 +2170,57 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
// JSDoc
|
||||
|
||||
/* @internal */
|
||||
export function createJSDocTypeExpression(type: TypeNode): JSDocTypeExpression {
|
||||
const node = createSynthesizedNode(SyntaxKind.JSDocTypeExpression) as JSDocTypeExpression;
|
||||
node.type = type;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createJSDocTypeTag(typeExpression?: JSDocTypeExpression, comment?: string): JSDocTypeTag {
|
||||
const tag = createJSDocTag<JSDocTypeTag>(SyntaxKind.JSDocTypeTag, "type");
|
||||
tag.typeExpression = typeExpression;
|
||||
tag.comment = comment;
|
||||
return tag;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createJSDocReturnTag(typeExpression?: JSDocTypeExpression, comment?: string): JSDocReturnTag {
|
||||
const tag = createJSDocTag<JSDocReturnTag>(SyntaxKind.JSDocReturnTag, "returns");
|
||||
tag.typeExpression = typeExpression;
|
||||
tag.comment = comment;
|
||||
return tag;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createJSDocParamTag(name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, comment?: string): JSDocParameterTag {
|
||||
const tag = createJSDocTag<JSDocParameterTag>(SyntaxKind.JSDocParameterTag, "param");
|
||||
tag.typeExpression = typeExpression;
|
||||
tag.name = name;
|
||||
tag.isBracketed = isBracketed;
|
||||
tag.comment = comment;
|
||||
return tag;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createJSDocComment(comment?: string | undefined, tags?: NodeArray<JSDocTag> | undefined) {
|
||||
const node = createSynthesizedNode(SyntaxKind.JSDocComment) as JSDoc;
|
||||
node.comment = comment;
|
||||
node.tags = tags;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
function createJSDocTag<T extends JSDocTag>(kind: T["kind"], tagName: string): T {
|
||||
const node = createSynthesizedNode(kind) as T;
|
||||
node.atToken = createToken(SyntaxKind.AtToken);
|
||||
node.tagName = createIdentifier(tagName);
|
||||
return node;
|
||||
}
|
||||
|
||||
// JSX
|
||||
|
||||
export function createJsxElement(openingElement: JsxOpeningElement, children: ReadonlyArray<JsxChild>, closingElement: JsxClosingElement) {
|
||||
|
||||
+19
-17
@@ -2447,7 +2447,7 @@ namespace ts {
|
||||
|
||||
export interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
constraint: TypeNode | undefined;
|
||||
constraint: JSDocTypeExpression | undefined;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
}
|
||||
|
||||
@@ -5532,33 +5532,34 @@ namespace ts {
|
||||
BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited.
|
||||
AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited.
|
||||
CommaDelimited = 1 << 4, // Each list item is comma (",") delimited.
|
||||
DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited,
|
||||
AsteriskDelimited = 1 << 5, // Each list item is asterisk ("\n *") delimited, used with JSDoc.
|
||||
DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited | AsteriskDelimited,
|
||||
|
||||
AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present.
|
||||
AllowTrailingComma = 1 << 6, // Write a trailing comma (",") if present.
|
||||
|
||||
// Whitespace
|
||||
Indented = 1 << 6, // The list should be indented.
|
||||
SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace.
|
||||
SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node.
|
||||
Indented = 1 << 7, // The list should be indented.
|
||||
SpaceBetweenBraces = 1 << 8, // Inserts a space after the opening brace and before the closing brace.
|
||||
SpaceBetweenSiblings = 1 << 9, // Inserts a space between each sibling node.
|
||||
|
||||
// Brackets/Braces
|
||||
Braces = 1 << 9, // The list is surrounded by "{" and "}".
|
||||
Parenthesis = 1 << 10, // The list is surrounded by "(" and ")".
|
||||
AngleBrackets = 1 << 11, // The list is surrounded by "<" and ">".
|
||||
SquareBrackets = 1 << 12, // The list is surrounded by "[" and "]".
|
||||
Braces = 1 << 10, // The list is surrounded by "{" and "}".
|
||||
Parenthesis = 1 << 11, // The list is surrounded by "(" and ")".
|
||||
AngleBrackets = 1 << 12, // The list is surrounded by "<" and ">".
|
||||
SquareBrackets = 1 << 13, // The list is surrounded by "[" and "]".
|
||||
BracketsMask = Braces | Parenthesis | AngleBrackets | SquareBrackets,
|
||||
|
||||
OptionalIfUndefined = 1 << 13, // Do not emit brackets if the list is undefined.
|
||||
OptionalIfEmpty = 1 << 14, // Do not emit brackets if the list is empty.
|
||||
OptionalIfUndefined = 1 << 14, // Do not emit brackets if the list is undefined.
|
||||
OptionalIfEmpty = 1 << 15, // Do not emit brackets if the list is empty.
|
||||
Optional = OptionalIfUndefined | OptionalIfEmpty,
|
||||
|
||||
// Other
|
||||
PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes.
|
||||
NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list.
|
||||
NoInterveningComments = 1 << 17, // Do not emit comments between each node
|
||||
PreferNewLine = 1 << 16, // Prefer adding a LineTerminator between synthesized nodes.
|
||||
NoTrailingNewLine = 1 << 17, // Do not emit a trailing NewLine for a MultiLine list.
|
||||
NoInterveningComments = 1 << 18, // Do not emit comments between each node
|
||||
|
||||
NoSpaceIfEmpty = 1 << 18, // If the literal is empty, do not add spaces between braces.
|
||||
SingleElement = 1 << 19,
|
||||
NoSpaceIfEmpty = 1 << 19, // If the literal is empty, do not add spaces between braces.
|
||||
SingleElement = 1 << 20,
|
||||
|
||||
// Precomputed Formats
|
||||
Modifiers = SingleLine | SpaceBetweenSiblings | NoInterveningComments,
|
||||
@@ -5598,6 +5599,7 @@ namespace ts {
|
||||
TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | AngleBrackets | Optional,
|
||||
Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
|
||||
IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets,
|
||||
JSDocComment = MultiLine | AsteriskDelimited,
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@@ -73,7 +73,9 @@ namespace ts.codefix {
|
||||
const type = inferTypeForVariableFromUsage(parent.name, program, cancellationToken);
|
||||
const typeNode = type && getTypeNodeIfAccessible(type, parent, program, host);
|
||||
if (typeNode) {
|
||||
changes.tryInsertJSDocType(sourceFile, parent, typeNode);
|
||||
// Note that the codefix will never fire with an existing `@type` tag, so there is no need to merge tags
|
||||
const typeTag = createJSDocTypeTag(createJSDocTypeExpression(typeNode), /*comment*/ "");
|
||||
addJSDocTags(changes, sourceFile, cast(parent.parent.parent, isExpressionStatement), [typeTag]);
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
@@ -192,7 +194,13 @@ namespace ts.codefix {
|
||||
const typeNode = type && getTypeNodeIfAccessible(type, declaration, program, host);
|
||||
if (typeNode) {
|
||||
if (isInJSFile(sourceFile) && declaration.kind !== SyntaxKind.PropertySignature) {
|
||||
changes.tryInsertJSDocType(sourceFile, declaration, typeNode);
|
||||
const parent = isVariableDeclaration(declaration) ? tryCast(declaration.parent.parent, isVariableStatement) : declaration;
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
const typeExpression = createJSDocTypeExpression(typeNode);
|
||||
const typeTag = isGetAccessorDeclaration(declaration) ? createJSDocReturnTag(typeExpression, "") : createJSDocTypeTag(typeExpression, "");
|
||||
addJSDocTags(changes, sourceFile, parent, [typeTag]);
|
||||
}
|
||||
else {
|
||||
changes.tryInsertTypeAnnotation(sourceFile, declaration, typeNode);
|
||||
@@ -200,16 +208,52 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function annotateJSDocParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterInferences: ParameterInference[], program: Program, host: LanguageServiceHost): void {
|
||||
const result = mapDefined(parameterInferences, inference => {
|
||||
function annotateJSDocParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterInferences: ReadonlyArray<ParameterInference>, program: Program, host: LanguageServiceHost): void {
|
||||
const signature = parameterInferences.length && parameterInferences[0].declaration.parent;
|
||||
if (!signature) {
|
||||
return;
|
||||
}
|
||||
const paramTags = mapDefined(parameterInferences, inference => {
|
||||
const param = inference.declaration;
|
||||
// only infer parameters that have (1) no type and (2) an accessible inferred type
|
||||
if (param.initializer || getJSDocType(param) || !isIdentifier(param.name)) return;
|
||||
|
||||
const typeNode = inference.type && getTypeNodeIfAccessible(inference.type, param, program, host);
|
||||
return typeNode && !param.initializer && !getJSDocType(param) ? { ...inference, typeNode } : undefined;
|
||||
return typeNode && createJSDocParamTag(param.name, !!inference.isOptional, createJSDocTypeExpression(typeNode), "");
|
||||
});
|
||||
changes.tryInsertJSDocParameters(sourceFile, result);
|
||||
addJSDocTags(changes, sourceFile, signature, paramTags);
|
||||
}
|
||||
|
||||
function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined {
|
||||
function addJSDocTags(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parent: HasJSDoc, newTags: ReadonlyArray<JSDocTag>): void {
|
||||
const comments = mapDefined(parent.jsDoc, j => j.comment);
|
||||
const oldTags = flatMap(parent.jsDoc, j => j.tags);
|
||||
const unmergedNewTags = newTags.filter(newTag => !oldTags || !oldTags.some((tag, i) => {
|
||||
const merged = tryMergeJsdocTags(tag, newTag);
|
||||
if (merged) oldTags[i] = merged;
|
||||
return !!merged;
|
||||
}));
|
||||
const tag = createJSDocComment(comments.join("\n"), createNodeArray([...(oldTags || emptyArray), ...unmergedNewTags]));
|
||||
changes.insertJsdocCommentBefore(sourceFile, parent, tag);
|
||||
}
|
||||
|
||||
function tryMergeJsdocTags(oldTag: JSDocTag, newTag: JSDocTag): JSDocTag | undefined {
|
||||
if (oldTag.kind !== newTag.kind) {
|
||||
return undefined;
|
||||
}
|
||||
switch (oldTag.kind) {
|
||||
case SyntaxKind.JSDocParameterTag: {
|
||||
const oldParam = oldTag as JSDocParameterTag;
|
||||
const newParam = newTag as JSDocParameterTag;
|
||||
return isIdentifier(oldParam.name) && isIdentifier(newParam.name) && oldParam.name.escapedText === newParam.name.escapedText
|
||||
? createJSDocParamTag(newParam.name, newParam.isBracketed, newParam.typeExpression, oldParam.comment)
|
||||
: undefined;
|
||||
}
|
||||
case SyntaxKind.JSDocReturnTag:
|
||||
return createJSDocReturnTag((newTag as JSDocReturnTag).typeExpression, oldTag.comment);
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined {
|
||||
const checker = program.getTypeChecker();
|
||||
let typeIsAccessible = true;
|
||||
const notAccessible = () => { typeIsAccessible = false; };
|
||||
|
||||
@@ -365,16 +365,21 @@ namespace ts.formatting {
|
||||
function formatSpan(originalRange: TextRange, sourceFile: SourceFile, formatContext: FormatContext, requestKind: FormattingRequestKind): TextChange[] {
|
||||
// find the smallest node that fully wraps the range and compute the initial indentation for the node
|
||||
const enclosingNode = findEnclosingNode(originalRange, sourceFile);
|
||||
return getFormattingScanner(sourceFile.text, sourceFile.languageVariant, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end, scanner => formatSpanWorker(
|
||||
originalRange,
|
||||
enclosingNode,
|
||||
SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, formatContext.options),
|
||||
getOwnOrInheritedDelta(enclosingNode, formatContext.options, sourceFile),
|
||||
scanner,
|
||||
formatContext,
|
||||
requestKind,
|
||||
prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange),
|
||||
sourceFile));
|
||||
return getFormattingScanner(
|
||||
sourceFile.text,
|
||||
sourceFile.languageVariant,
|
||||
getScanStartPosition(enclosingNode, originalRange, sourceFile),
|
||||
originalRange.end,
|
||||
scanner => formatSpanWorker(
|
||||
originalRange,
|
||||
enclosingNode,
|
||||
SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, formatContext.options),
|
||||
getOwnOrInheritedDelta(enclosingNode, formatContext.options, sourceFile),
|
||||
scanner,
|
||||
formatContext,
|
||||
requestKind,
|
||||
prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange),
|
||||
sourceFile));
|
||||
}
|
||||
|
||||
function formatSpanWorker(originalRange: TextRange,
|
||||
@@ -413,7 +418,8 @@ namespace ts.formatting {
|
||||
if (!formattingScanner.isOnToken()) {
|
||||
const leadingTrivia = formattingScanner.getCurrentLeadingTrivia();
|
||||
if (leadingTrivia) {
|
||||
processTrivia(leadingTrivia, enclosingNode, enclosingNode, /*dynamicIndentation*/ undefined!); // TODO: GH#18217
|
||||
indentTriviaItems(leadingTrivia, initialIndentation, /*indentNextTokenOrTrivia*/ false,
|
||||
item => processRange(item, sourceFile.getLineAndCharacterOfPosition(item.pos), enclosingNode, enclosingNode, /*dynamicIndentation*/ undefined!));
|
||||
trimTrailingWhitespacesForRemainingRange();
|
||||
}
|
||||
}
|
||||
@@ -814,27 +820,8 @@ namespace ts.formatting {
|
||||
let indentNextTokenOrTrivia = true;
|
||||
if (currentTokenInfo.leadingTrivia) {
|
||||
const commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container);
|
||||
|
||||
for (const triviaItem of currentTokenInfo.leadingTrivia) {
|
||||
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
|
||||
switch (triviaItem.kind) {
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
if (triviaInRange) {
|
||||
indentMultilineCommentOrJsxText(triviaItem, commentIndentation, /*firstLineIsIndented*/ !indentNextTokenOrTrivia);
|
||||
}
|
||||
indentNextTokenOrTrivia = false;
|
||||
break;
|
||||
case SyntaxKind.SingleLineCommentTrivia:
|
||||
if (indentNextTokenOrTrivia && triviaInRange) {
|
||||
insertIndentation(triviaItem.pos, commentIndentation, /*lineAdded*/ false);
|
||||
}
|
||||
indentNextTokenOrTrivia = false;
|
||||
break;
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
indentNextTokenOrTrivia = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
indentNextTokenOrTrivia = indentTriviaItems(currentTokenInfo.leadingTrivia, commentIndentation, indentNextTokenOrTrivia,
|
||||
item => insertIndentation(item.pos, commentIndentation, /*lineAdded*/ false));
|
||||
}
|
||||
|
||||
// indent token only if is it is in target range and does not overlap with any error ranges
|
||||
@@ -852,6 +839,34 @@ namespace ts.formatting {
|
||||
}
|
||||
}
|
||||
|
||||
function indentTriviaItems(
|
||||
trivia: TextRangeWithKind[],
|
||||
commentIndentation: number,
|
||||
indentNextTokenOrTrivia: boolean,
|
||||
indentSingleLine: (item: TextRangeWithKind) => void) {
|
||||
for (const triviaItem of trivia) {
|
||||
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
|
||||
switch (triviaItem.kind) {
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
if (triviaInRange) {
|
||||
indentMultilineCommentOrJsxText(triviaItem, commentIndentation, /*firstLineIsIndented*/ !indentNextTokenOrTrivia);
|
||||
}
|
||||
indentNextTokenOrTrivia = false;
|
||||
break;
|
||||
case SyntaxKind.SingleLineCommentTrivia:
|
||||
if (indentNextTokenOrTrivia && triviaInRange) {
|
||||
indentSingleLine(triviaItem);
|
||||
}
|
||||
indentNextTokenOrTrivia = false;
|
||||
break;
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
indentNextTokenOrTrivia = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return indentNextTokenOrTrivia;
|
||||
}
|
||||
|
||||
function processTrivia(trivia: TextRangeWithKind[], parent: Node, contextNode: Node, dynamicIndentation: DynamicIndentation): void {
|
||||
for (const triviaItem of trivia) {
|
||||
if (isComment(triviaItem.kind) && rangeContainsRange(originalRange, triviaItem)) {
|
||||
@@ -861,7 +876,6 @@ namespace ts.formatting {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: GH#18217 use an enum instead of `boolean | undefined`
|
||||
function processRange(range: TextRangeWithKind,
|
||||
rangeStart: LineAndCharacter,
|
||||
parent: Node,
|
||||
@@ -1072,7 +1086,10 @@ namespace ts.formatting {
|
||||
* Trimming will be done for lines after the previous range
|
||||
*/
|
||||
function trimTrailingWhitespacesForRemainingRange() {
|
||||
const startPosition = previousRange ? previousRange.end : originalRange.pos;
|
||||
if (!previousRange) {
|
||||
return;
|
||||
}
|
||||
const startPosition = previousRange.end;
|
||||
|
||||
const startLine = sourceFile.getLineAndCharacterOfPosition(startPosition).line;
|
||||
const endLine = sourceFile.getLineAndCharacterOfPosition(originalRange.end).line;
|
||||
|
||||
@@ -79,14 +79,14 @@ namespace ts.formatting {
|
||||
return findFirstNonWhitespaceColumn(getStartPositionOfLine(commentStartLine, sourceFile), position, sourceFile, options);
|
||||
}
|
||||
|
||||
const startPostionOfLine = getStartPositionOfLine(previousLine, sourceFile);
|
||||
const { column, character } = findFirstNonWhitespaceCharacterAndColumn(startPostionOfLine, position, sourceFile, options);
|
||||
const startPositionOfLine = getStartPositionOfLine(previousLine, sourceFile);
|
||||
const { column, character } = findFirstNonWhitespaceCharacterAndColumn(startPositionOfLine, position, sourceFile, options);
|
||||
|
||||
if (column === 0) {
|
||||
return column;
|
||||
}
|
||||
|
||||
const firstNonWhitespaceCharacterCode = sourceFile.text.charCodeAt(startPostionOfLine + character);
|
||||
const firstNonWhitespaceCharacterCode = sourceFile.text.charCodeAt(startPositionOfLine + character);
|
||||
return firstNonWhitespaceCharacterCode === CharacterCodes.asterisk ? column - 1 : column;
|
||||
}
|
||||
|
||||
|
||||
+13
-48
@@ -209,12 +209,6 @@ namespace ts.textChanges {
|
||||
|
||||
export type TypeAnnotatable = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature;
|
||||
|
||||
interface JSDocParameter {
|
||||
declaration: ParameterDeclaration;
|
||||
typeNode: TypeNode;
|
||||
isOptional?: boolean;
|
||||
}
|
||||
|
||||
export class ChangeTracker {
|
||||
private readonly changes: Change[] = [];
|
||||
private readonly newFiles: { readonly oldFile: SourceFile | undefined, readonly fileName: string, readonly statements: ReadonlyArray<Statement> }[] = [];
|
||||
@@ -345,10 +339,19 @@ namespace ts.textChanges {
|
||||
this.insertText(sourceFile, token.getStart(sourceFile), text);
|
||||
}
|
||||
|
||||
public insertCommentThenNewline(sourceFile: SourceFile, character: number, position: number, commentText: string): void {
|
||||
const token = getTouchingToken(sourceFile, position);
|
||||
const text = "/**" + commentText + "*/" + this.newLineCharacter + repeatString(" ", character);
|
||||
this.insertText(sourceFile, token.getStart(sourceFile), text);
|
||||
public insertJsdocCommentBefore(sourceFile: SourceFile, node: HasJSDoc, tag: JSDoc) {
|
||||
const fnStart = node.getStart(sourceFile);
|
||||
if (node.jsDoc) {
|
||||
for (const jsdoc of node.jsDoc) {
|
||||
this.deleteRange(sourceFile, {
|
||||
pos: getLineStartPositionForPosition(jsdoc.getStart(sourceFile), sourceFile),
|
||||
end: getAdjustedEndPosition(sourceFile, jsdoc, /*options*/ {})
|
||||
});
|
||||
}
|
||||
}
|
||||
const startPosition = getPrecedingNonSpaceCharacterPosition(sourceFile.text, fnStart - 1);
|
||||
const indent = sourceFile.text.slice(startPosition, fnStart);
|
||||
this.insertNodeAt(sourceFile, fnStart, tag, { preserveLeadingWhitespace: false, suffix: this.newLineCharacter + indent });
|
||||
}
|
||||
|
||||
public replaceRangeWithText(sourceFile: SourceFile, range: TextRange, text: string) {
|
||||
@@ -359,23 +362,6 @@ namespace ts.textChanges {
|
||||
this.replaceRangeWithText(sourceFile, createRange(pos), text);
|
||||
}
|
||||
|
||||
public tryInsertJSDocParameters(sourceFile: SourceFile, parameters: JSDocParameter[]) {
|
||||
if (parameters.length === 0) {
|
||||
return;
|
||||
}
|
||||
const parent = parameters[0].declaration.parent;
|
||||
const indent = getLineAndCharacterOfPosition(sourceFile, parent.getStart()).character;
|
||||
let commentText = "\n";
|
||||
for (const { declaration, typeNode, isOptional } of parameters) {
|
||||
if (isIdentifier(declaration.name)) {
|
||||
const printed = changesToText.getNonformattedText(typeNode, sourceFile, this.newLineCharacter).text;
|
||||
commentText += this.printJSDocParameter(indent, printed, declaration.name, isOptional);
|
||||
}
|
||||
}
|
||||
commentText += repeatString(" ", indent + 1);
|
||||
this.insertCommentThenNewline(sourceFile, indent, parent.getStart(), commentText);
|
||||
}
|
||||
|
||||
/** Prefer this over replacing a node with another that has a type annotation, as it avoids reformatting the other parts of the node. */
|
||||
public tryInsertTypeAnnotation(sourceFile: SourceFile, node: TypeAnnotatable, type: TypeNode): void {
|
||||
let endNode: Node | undefined;
|
||||
@@ -394,27 +380,6 @@ namespace ts.textChanges {
|
||||
this.insertNodeAt(sourceFile, endNode.end, type, { prefix: ": " });
|
||||
}
|
||||
|
||||
public tryInsertJSDocType(sourceFile: SourceFile, node: Node, type: TypeNode): void {
|
||||
const printed = changesToText.getNonformattedText(type, sourceFile, this.newLineCharacter).text;
|
||||
let commentText;
|
||||
if (isGetAccessorDeclaration(node)) {
|
||||
commentText = ` @return {${printed}} `;
|
||||
}
|
||||
else {
|
||||
commentText = ` @type {${printed}} `;
|
||||
node = node.parent;
|
||||
}
|
||||
this.insertCommentThenNewline(sourceFile, getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).character, node.getStart(sourceFile), commentText);
|
||||
}
|
||||
|
||||
private printJSDocParameter(indent: number, printed: string, name: Identifier, isOptionalParameter: boolean | undefined) {
|
||||
let printName = unescapeLeadingUnderscores(name.escapedText);
|
||||
if (isOptionalParameter) {
|
||||
printName = `[${printName}]`;
|
||||
}
|
||||
return repeatString(" ", indent) + ` * @param {${printed}} ${printName}\n`;
|
||||
}
|
||||
|
||||
public insertTypeParameters(sourceFile: SourceFile, node: SignatureDeclaration, typeParameters: ReadonlyArray<TypeParameterDeclaration>): void {
|
||||
// If no `(`, is an arrow function `x => x`, so use the pos of the first parameter
|
||||
const start = (findChildOfKind(node, SyntaxKind.OpenParenToken, sourceFile) || first(node.parameters)).getStart(sourceFile);
|
||||
|
||||
@@ -1672,6 +1672,13 @@ namespace ts {
|
||||
return position;
|
||||
}
|
||||
|
||||
export function getPrecedingNonSpaceCharacterPosition(text: string, position: number) {
|
||||
while (position > -1 && isWhiteSpaceSingleLine(text.charCodeAt(position))) {
|
||||
position -= 1;
|
||||
}
|
||||
return position + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deep, memberwise clone of a node with no source map location.
|
||||
*
|
||||
|
||||
+55
-53
@@ -1573,7 +1573,7 @@ declare namespace ts {
|
||||
}
|
||||
interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
constraint: TypeNode | undefined;
|
||||
constraint: JSDocTypeExpression | undefined;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
}
|
||||
interface JSDocReturnTag extends JSDocTag {
|
||||
@@ -2942,60 +2942,62 @@ declare namespace ts {
|
||||
BarDelimited = 4,
|
||||
AmpersandDelimited = 8,
|
||||
CommaDelimited = 16,
|
||||
DelimitersMask = 28,
|
||||
AllowTrailingComma = 32,
|
||||
Indented = 64,
|
||||
SpaceBetweenBraces = 128,
|
||||
SpaceBetweenSiblings = 256,
|
||||
Braces = 512,
|
||||
Parenthesis = 1024,
|
||||
AngleBrackets = 2048,
|
||||
SquareBrackets = 4096,
|
||||
BracketsMask = 7680,
|
||||
OptionalIfUndefined = 8192,
|
||||
OptionalIfEmpty = 16384,
|
||||
Optional = 24576,
|
||||
PreferNewLine = 32768,
|
||||
NoTrailingNewLine = 65536,
|
||||
NoInterveningComments = 131072,
|
||||
NoSpaceIfEmpty = 262144,
|
||||
SingleElement = 524288,
|
||||
Modifiers = 131328,
|
||||
HeritageClauses = 256,
|
||||
SingleLineTypeLiteralMembers = 384,
|
||||
MultiLineTypeLiteralMembers = 16449,
|
||||
TupleTypeElements = 272,
|
||||
UnionTypeConstituents = 260,
|
||||
IntersectionTypeConstituents = 264,
|
||||
ObjectBindingPatternElements = 262576,
|
||||
ArrayBindingPatternElements = 262448,
|
||||
ObjectLiteralExpressionProperties = 263122,
|
||||
ArrayLiteralExpressionElements = 4466,
|
||||
CommaListElements = 272,
|
||||
CallExpressionArguments = 1296,
|
||||
NewExpressionArguments = 9488,
|
||||
TemplateExpressionSpans = 131072,
|
||||
SingleLineBlockStatements = 384,
|
||||
MultiLineBlockStatements = 65,
|
||||
VariableDeclarationList = 272,
|
||||
SingleLineFunctionBodyStatements = 384,
|
||||
AsteriskDelimited = 32,
|
||||
DelimitersMask = 60,
|
||||
AllowTrailingComma = 64,
|
||||
Indented = 128,
|
||||
SpaceBetweenBraces = 256,
|
||||
SpaceBetweenSiblings = 512,
|
||||
Braces = 1024,
|
||||
Parenthesis = 2048,
|
||||
AngleBrackets = 4096,
|
||||
SquareBrackets = 8192,
|
||||
BracketsMask = 15360,
|
||||
OptionalIfUndefined = 16384,
|
||||
OptionalIfEmpty = 32768,
|
||||
Optional = 49152,
|
||||
PreferNewLine = 65536,
|
||||
NoTrailingNewLine = 131072,
|
||||
NoInterveningComments = 262144,
|
||||
NoSpaceIfEmpty = 524288,
|
||||
SingleElement = 1048576,
|
||||
Modifiers = 262656,
|
||||
HeritageClauses = 512,
|
||||
SingleLineTypeLiteralMembers = 768,
|
||||
MultiLineTypeLiteralMembers = 32897,
|
||||
TupleTypeElements = 528,
|
||||
UnionTypeConstituents = 516,
|
||||
IntersectionTypeConstituents = 520,
|
||||
ObjectBindingPatternElements = 525136,
|
||||
ArrayBindingPatternElements = 524880,
|
||||
ObjectLiteralExpressionProperties = 526226,
|
||||
ArrayLiteralExpressionElements = 8914,
|
||||
CommaListElements = 528,
|
||||
CallExpressionArguments = 2576,
|
||||
NewExpressionArguments = 18960,
|
||||
TemplateExpressionSpans = 262144,
|
||||
SingleLineBlockStatements = 768,
|
||||
MultiLineBlockStatements = 129,
|
||||
VariableDeclarationList = 528,
|
||||
SingleLineFunctionBodyStatements = 768,
|
||||
MultiLineFunctionBodyStatements = 1,
|
||||
ClassHeritageClauses = 0,
|
||||
ClassMembers = 65,
|
||||
InterfaceMembers = 65,
|
||||
EnumMembers = 81,
|
||||
CaseBlockClauses = 65,
|
||||
NamedImportsOrExportsElements = 262576,
|
||||
JsxElementOrFragmentChildren = 131072,
|
||||
JsxElementAttributes = 131328,
|
||||
CaseOrDefaultClauseStatements = 81985,
|
||||
HeritageClauseTypes = 272,
|
||||
SourceFileStatements = 65537,
|
||||
Decorators = 24577,
|
||||
TypeArguments = 26896,
|
||||
TypeParameters = 26896,
|
||||
Parameters = 1296,
|
||||
IndexSignatureParameters = 4432
|
||||
ClassMembers = 129,
|
||||
InterfaceMembers = 129,
|
||||
EnumMembers = 145,
|
||||
CaseBlockClauses = 129,
|
||||
NamedImportsOrExportsElements = 525136,
|
||||
JsxElementOrFragmentChildren = 262144,
|
||||
JsxElementAttributes = 262656,
|
||||
CaseOrDefaultClauseStatements = 163969,
|
||||
HeritageClauseTypes = 528,
|
||||
SourceFileStatements = 131073,
|
||||
Decorators = 49153,
|
||||
TypeArguments = 53776,
|
||||
TypeParameters = 53776,
|
||||
Parameters = 2576,
|
||||
IndexSignatureParameters = 8848,
|
||||
JSDocComment = 33
|
||||
}
|
||||
interface UserPreferences {
|
||||
readonly disableSuggestions?: boolean;
|
||||
|
||||
+55
-53
@@ -1573,7 +1573,7 @@ declare namespace ts {
|
||||
}
|
||||
interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
constraint: TypeNode | undefined;
|
||||
constraint: JSDocTypeExpression | undefined;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
}
|
||||
interface JSDocReturnTag extends JSDocTag {
|
||||
@@ -2942,60 +2942,62 @@ declare namespace ts {
|
||||
BarDelimited = 4,
|
||||
AmpersandDelimited = 8,
|
||||
CommaDelimited = 16,
|
||||
DelimitersMask = 28,
|
||||
AllowTrailingComma = 32,
|
||||
Indented = 64,
|
||||
SpaceBetweenBraces = 128,
|
||||
SpaceBetweenSiblings = 256,
|
||||
Braces = 512,
|
||||
Parenthesis = 1024,
|
||||
AngleBrackets = 2048,
|
||||
SquareBrackets = 4096,
|
||||
BracketsMask = 7680,
|
||||
OptionalIfUndefined = 8192,
|
||||
OptionalIfEmpty = 16384,
|
||||
Optional = 24576,
|
||||
PreferNewLine = 32768,
|
||||
NoTrailingNewLine = 65536,
|
||||
NoInterveningComments = 131072,
|
||||
NoSpaceIfEmpty = 262144,
|
||||
SingleElement = 524288,
|
||||
Modifiers = 131328,
|
||||
HeritageClauses = 256,
|
||||
SingleLineTypeLiteralMembers = 384,
|
||||
MultiLineTypeLiteralMembers = 16449,
|
||||
TupleTypeElements = 272,
|
||||
UnionTypeConstituents = 260,
|
||||
IntersectionTypeConstituents = 264,
|
||||
ObjectBindingPatternElements = 262576,
|
||||
ArrayBindingPatternElements = 262448,
|
||||
ObjectLiteralExpressionProperties = 263122,
|
||||
ArrayLiteralExpressionElements = 4466,
|
||||
CommaListElements = 272,
|
||||
CallExpressionArguments = 1296,
|
||||
NewExpressionArguments = 9488,
|
||||
TemplateExpressionSpans = 131072,
|
||||
SingleLineBlockStatements = 384,
|
||||
MultiLineBlockStatements = 65,
|
||||
VariableDeclarationList = 272,
|
||||
SingleLineFunctionBodyStatements = 384,
|
||||
AsteriskDelimited = 32,
|
||||
DelimitersMask = 60,
|
||||
AllowTrailingComma = 64,
|
||||
Indented = 128,
|
||||
SpaceBetweenBraces = 256,
|
||||
SpaceBetweenSiblings = 512,
|
||||
Braces = 1024,
|
||||
Parenthesis = 2048,
|
||||
AngleBrackets = 4096,
|
||||
SquareBrackets = 8192,
|
||||
BracketsMask = 15360,
|
||||
OptionalIfUndefined = 16384,
|
||||
OptionalIfEmpty = 32768,
|
||||
Optional = 49152,
|
||||
PreferNewLine = 65536,
|
||||
NoTrailingNewLine = 131072,
|
||||
NoInterveningComments = 262144,
|
||||
NoSpaceIfEmpty = 524288,
|
||||
SingleElement = 1048576,
|
||||
Modifiers = 262656,
|
||||
HeritageClauses = 512,
|
||||
SingleLineTypeLiteralMembers = 768,
|
||||
MultiLineTypeLiteralMembers = 32897,
|
||||
TupleTypeElements = 528,
|
||||
UnionTypeConstituents = 516,
|
||||
IntersectionTypeConstituents = 520,
|
||||
ObjectBindingPatternElements = 525136,
|
||||
ArrayBindingPatternElements = 524880,
|
||||
ObjectLiteralExpressionProperties = 526226,
|
||||
ArrayLiteralExpressionElements = 8914,
|
||||
CommaListElements = 528,
|
||||
CallExpressionArguments = 2576,
|
||||
NewExpressionArguments = 18960,
|
||||
TemplateExpressionSpans = 262144,
|
||||
SingleLineBlockStatements = 768,
|
||||
MultiLineBlockStatements = 129,
|
||||
VariableDeclarationList = 528,
|
||||
SingleLineFunctionBodyStatements = 768,
|
||||
MultiLineFunctionBodyStatements = 1,
|
||||
ClassHeritageClauses = 0,
|
||||
ClassMembers = 65,
|
||||
InterfaceMembers = 65,
|
||||
EnumMembers = 81,
|
||||
CaseBlockClauses = 65,
|
||||
NamedImportsOrExportsElements = 262576,
|
||||
JsxElementOrFragmentChildren = 131072,
|
||||
JsxElementAttributes = 131328,
|
||||
CaseOrDefaultClauseStatements = 81985,
|
||||
HeritageClauseTypes = 272,
|
||||
SourceFileStatements = 65537,
|
||||
Decorators = 24577,
|
||||
TypeArguments = 26896,
|
||||
TypeParameters = 26896,
|
||||
Parameters = 1296,
|
||||
IndexSignatureParameters = 4432
|
||||
ClassMembers = 129,
|
||||
InterfaceMembers = 129,
|
||||
EnumMembers = 145,
|
||||
CaseBlockClauses = 129,
|
||||
NamedImportsOrExportsElements = 525136,
|
||||
JsxElementOrFragmentChildren = 262144,
|
||||
JsxElementAttributes = 262656,
|
||||
CaseOrDefaultClauseStatements = 163969,
|
||||
HeritageClauseTypes = 528,
|
||||
SourceFileStatements = 131073,
|
||||
Decorators = 49153,
|
||||
TypeArguments = 53776,
|
||||
TypeParameters = 53776,
|
||||
Parameters = 2576,
|
||||
IndexSignatureParameters = 8848,
|
||||
JSDocComment = 33
|
||||
}
|
||||
interface UserPreferences {
|
||||
readonly disableSuggestions?: boolean;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noImplicitAny: true
|
||||
// @strictNullChecks: false
|
||||
// @Filename: important.js
|
||||
|
||||
/////** @param x no types here! */
|
||||
/////**
|
||||
//// * 1
|
||||
//// * @param x a duplicate!
|
||||
//// * @param y yy
|
||||
//// */
|
||||
/////**
|
||||
//// * 2
|
||||
//// * @param z zz
|
||||
//// */
|
||||
////function f(x) {
|
||||
//// return x * 1
|
||||
////}
|
||||
////
|
||||
////var o = {
|
||||
//// /** 1
|
||||
//// * @return First one
|
||||
//// */
|
||||
//// // intrusive comment (should not be deleted)
|
||||
//// /** 2
|
||||
//// * @see also
|
||||
//// */
|
||||
//// /** 3
|
||||
//// * @return Second one
|
||||
//// * @extends {C<number>} nothing really
|
||||
//// * @class
|
||||
//// * @this {*} doesn't make sense here
|
||||
//// * @enum wat
|
||||
//// */
|
||||
//// /**
|
||||
//// * @typedef {number} Meter or something
|
||||
//// * @typedef {Object} Position Comment!
|
||||
//// * @property {number} x what a horrible place for a type
|
||||
//// * @property {number} y please don't do this
|
||||
//// */
|
||||
//// /**
|
||||
//// * @template {string} T postfix comment
|
||||
//// * @callback Action not sure what this will do
|
||||
//// * @param {T} thing
|
||||
//// * @returns {string} oh no
|
||||
//// */
|
||||
//// get m() { return undefined }
|
||||
////}
|
||||
////o.m = 1
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "inferFromUsage",
|
||||
fixAllDescription: "Infer all types from usage",
|
||||
newFileContent:
|
||||
`/**
|
||||
* 1
|
||||
* 2
|
||||
* @param {number} x no types here!
|
||||
* @param x a duplicate!
|
||||
* @param y yy
|
||||
* @param z zz
|
||||
*/
|
||||
function f(x) {
|
||||
return x * 1
|
||||
}
|
||||
|
||||
var o = {
|
||||
// intrusive comment (should not be deleted)
|
||||
/**
|
||||
* 1
|
||||
* 2
|
||||
* 3
|
||||
* @returns {number} First one
|
||||
* @see also
|
||||
* @return Second one
|
||||
* @extends {C<number>} nothing really
|
||||
* @class
|
||||
* @this {*} doesn't make sense here
|
||||
* @enum {wat}
|
||||
* @typedef {number} Meter or something
|
||||
* @typedef {Object} Position Comment!
|
||||
* @property {number} x what a horrible place for a type
|
||||
* @property {number} y please don't do this
|
||||
* @template {string} T postfix comment
|
||||
* @callback Action not sure what this will do
|
||||
* @param {T} thing
|
||||
* @returns {string} oh no
|
||||
*/
|
||||
get m() { return undefined }
|
||||
}
|
||||
o.m = 1`,
|
||||
});
|
||||
|
||||
@@ -7,26 +7,34 @@
|
||||
// @Filename: important.js
|
||||
////class C {
|
||||
//// constructor() {
|
||||
//// [|this.p|] = undefined;
|
||||
//// /** this is fine */
|
||||
//// this.p = undefined;
|
||||
//// this.q = undefined
|
||||
//// }
|
||||
//// method() {
|
||||
//// this.p.push(1)
|
||||
//// this.q.push(1);
|
||||
//// }
|
||||
////}
|
||||
|
||||
// Note: Should be number[] | undefined, but inference currently privileges assignments
|
||||
// over usage (even when the only result is undefined) and infers only undefined.
|
||||
verify.codeFix({
|
||||
description: "Infer type of 'p' from usage",
|
||||
index: 2,
|
||||
verify.codeFixAll({
|
||||
fixId: "inferFromUsage",
|
||||
fixAllDescription: "Infer all types from usage",
|
||||
newFileContent:
|
||||
`class C {
|
||||
constructor() {
|
||||
/** @type {undefined} */
|
||||
/**
|
||||
* this is fine
|
||||
* @type {undefined}
|
||||
*/
|
||||
this.p = undefined;
|
||||
/** @type {undefined} */
|
||||
this.q = undefined
|
||||
}
|
||||
method() {
|
||||
this.p.push(1)
|
||||
this.q.push(1);
|
||||
}
|
||||
}`
|
||||
});
|
||||
}`});
|
||||
|
||||
@@ -19,8 +19,6 @@ verify.codeFix({
|
||||
newFileContent:
|
||||
`/**
|
||||
* @param {*} y
|
||||
*/
|
||||
/**
|
||||
* @param {number} x
|
||||
* @param {number} z
|
||||
*/
|
||||
|
||||
@@ -17,8 +17,8 @@ verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 2,
|
||||
newFileContent:
|
||||
`/** @param {number} a */
|
||||
/**
|
||||
`/**
|
||||
* @param {number} a
|
||||
* @param {(string | boolean)[]} rest
|
||||
*/
|
||||
function f(a, ...rest){
|
||||
|
||||
@@ -14,8 +14,8 @@ verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 2,
|
||||
newFileContent:
|
||||
`/** @param {number} a */
|
||||
/**
|
||||
`/**
|
||||
* @param {number} a
|
||||
* @param {number[]} rest
|
||||
*/
|
||||
function f(a, ...rest){
|
||||
|
||||
@@ -17,8 +17,8 @@ verify.codeFix({
|
||||
description: "Infer parameter types from usage",
|
||||
index: 4,
|
||||
newFileContent:
|
||||
`/** @param {number} a */
|
||||
/**
|
||||
`/**
|
||||
* @param {number} a
|
||||
* @param {string[]} rest
|
||||
*/
|
||||
function f(a: number, ...rest){
|
||||
|
||||
@@ -14,9 +14,9 @@ verify.codeFix({
|
||||
index: 2,
|
||||
newFileContent:
|
||||
`class C {/**
|
||||
* @param {number} x
|
||||
*/
|
||||
m(x) {return x;}}
|
||||
* @param {number} x
|
||||
*/
|
||||
m(x) {return x;}}
|
||||
var c = new C()
|
||||
c.m(1)`,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
/////**
|
||||
//// * JSDoc for things
|
||||
//// */
|
||||
////function f() {
|
||||
//// /** more
|
||||
//// jsdoc */
|
||||
//// var t;
|
||||
//// /**
|
||||
//// * multiline
|
||||
//// */
|
||||
//// var multiline;
|
||||
////}
|
||||
|
||||
format.document();
|
||||
|
||||
verify.currentFileContentIs(`/**
|
||||
* JSDoc for things
|
||||
*/
|
||||
function f() {
|
||||
/** more
|
||||
jsdoc */
|
||||
var t;
|
||||
/**
|
||||
* multiline
|
||||
*/
|
||||
var multiline;
|
||||
}`);
|
||||
@@ -1,9 +1,9 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////
|
||||
////// whitespace below
|
||||
////// 1 below
|
||||
////
|
||||
////// whitespace above
|
||||
////// 2 above
|
||||
////
|
||||
////let x;
|
||||
////
|
||||
@@ -11,25 +11,25 @@
|
||||
////
|
||||
////let y;
|
||||
////
|
||||
////// whitespace above again
|
||||
////// 3 above
|
||||
////
|
||||
////while (true) {
|
||||
//// while (true) {
|
||||
//// }
|
||||
////
|
||||
//// // whitespace above
|
||||
//// // 4 above
|
||||
////}
|
||||
////
|
||||
////// whitespace above again
|
||||
////// 5 above
|
||||
////
|
||||
////
|
||||
|
||||
format.document();
|
||||
|
||||
verify.currentFileContentIs(`
|
||||
// whitespace below
|
||||
// 1 below
|
||||
|
||||
// whitespace above
|
||||
// 2 above
|
||||
|
||||
let x;
|
||||
|
||||
@@ -37,15 +37,15 @@ let x;
|
||||
|
||||
let y;
|
||||
|
||||
// whitespace above again
|
||||
// 3 above
|
||||
|
||||
while (true) {
|
||||
while (true) {
|
||||
}
|
||||
|
||||
// whitespace above
|
||||
// 4 above
|
||||
}
|
||||
|
||||
// whitespace above again
|
||||
// 5 above
|
||||
|
||||
`);
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
////function foo(a,
|
||||
//// /*2*/b,/*0*/
|
||||
//// //comment/*3*/
|
||||
//// //ABC/*3*/
|
||||
//// /*4*/c
|
||||
//// ) {
|
||||
////};
|
||||
////var x = [
|
||||
//// /*5*///comment/*1*/
|
||||
//// /*5*///DEF/*1*/
|
||||
//// 1,/*6*/
|
||||
//// 2/*7*/
|
||||
////]
|
||||
@@ -17,14 +17,14 @@ verify.indentationIs(4);
|
||||
goTo.marker("2");
|
||||
verify.currentLineContentIs(" b,");
|
||||
goTo.marker("3");
|
||||
verify.currentLineContentIs(" //comment");
|
||||
verify.currentLineContentIs(" //ABC");
|
||||
goTo.marker("4");
|
||||
verify.currentLineContentIs(" c");
|
||||
goTo.marker("1");
|
||||
edit.insert("\n");
|
||||
verify.indentationIs(4);
|
||||
goTo.marker("5");
|
||||
verify.currentLineContentIs(" //comment");
|
||||
verify.currentLineContentIs(" //DEF");
|
||||
goTo.marker("6");
|
||||
verify.currentLineContentIs(" 1,");
|
||||
goTo.marker("7");
|
||||
|
||||
Reference in New Issue
Block a user