Support services for @typedef (#16087)
* Support services for @typedef * Ensure JSDocTypeReference has SemanticMeaning.Type * Get SemanticMeaning right
This commit is contained in:
parent
6972766e91
commit
3cd9f3d2d4
|
@ -2102,6 +2102,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
export interface JSDocTag extends Node {
|
||||
parent: JSDoc;
|
||||
atToken: AtToken;
|
||||
tagName: Identifier;
|
||||
comment: string | undefined;
|
||||
|
@ -2132,6 +2133,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
export interface JSDocTypedefTag extends JSDocTag, NamedDeclaration {
|
||||
parent: JSDoc;
|
||||
kind: SyntaxKind.JSDocTypedefTag;
|
||||
fullName?: JSDocNamespaceDeclaration | Identifier;
|
||||
name?: Identifier;
|
||||
|
@ -2140,6 +2142,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
|
||||
parent: JSDoc;
|
||||
kind: SyntaxKind.JSDocPropertyTag;
|
||||
name: Identifier;
|
||||
typeExpression: JSDocTypeExpression;
|
||||
|
|
|
@ -288,6 +288,14 @@ namespace ts {
|
|||
return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode;
|
||||
}
|
||||
|
||||
export function isJSDoc(node: Node): node is JSDoc {
|
||||
return node.kind === SyntaxKind.JSDocComment;
|
||||
}
|
||||
|
||||
export function isJSDocTypedefTag(node: Node): node is JSDocTypedefTag {
|
||||
return node.kind === SyntaxKind.JSDocTypedefTag;
|
||||
}
|
||||
|
||||
export function isJSDocTag(node: Node) {
|
||||
return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode;
|
||||
}
|
||||
|
@ -1551,6 +1559,10 @@ namespace ts {
|
|||
}
|
||||
|
||||
export function getJSDocs(node: Node): (JSDoc | JSDocTag)[] {
|
||||
if (isJSDocTypedefTag(node)) {
|
||||
return [node.parent];
|
||||
}
|
||||
|
||||
let cache: (JSDoc | JSDocTag)[] = node.jsDocCache;
|
||||
if (!cache) {
|
||||
getJSDocsWorker(node);
|
||||
|
|
|
@ -784,7 +784,8 @@ namespace ts.FindAllReferences.Core {
|
|||
return;
|
||||
}
|
||||
|
||||
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, /*fullStart*/ state.options.findInComments || container.jsDoc !== undefined)) {
|
||||
const fullStart = state.options.findInComments || container.jsDoc !== undefined || forEach(search.symbol.declarations, d => d.kind === ts.SyntaxKind.JSDocTypedefTag);
|
||||
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, fullStart)) {
|
||||
getReferencesAtLocation(sourceFile, position, search, state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace ts.GoToDefinition {
|
|||
if (referenceFile) {
|
||||
return [getDefinitionInfoForFileReference(comment.fileName, referenceFile.fileName)];
|
||||
}
|
||||
return undefined;
|
||||
// Might still be on jsdoc, so keep looking.
|
||||
}
|
||||
|
||||
// Type reference directives
|
||||
|
|
|
@ -39,13 +39,14 @@ namespace ts {
|
|||
case SyntaxKind.TypeLiteral:
|
||||
return SemanticMeaning.Type;
|
||||
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
// If it has no name node, it shares the name with the value declaration below it.
|
||||
return (node as JSDocTypedefTag).name === undefined ? SemanticMeaning.Value | SemanticMeaning.Type : SemanticMeaning.Type;
|
||||
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type;
|
||||
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return SemanticMeaning.All;
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if (isAmbientModule(<ModuleDeclaration>node)) {
|
||||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
|
@ -57,6 +58,7 @@ namespace ts {
|
|||
return SemanticMeaning.Namespace;
|
||||
}
|
||||
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.NamedImports:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
|
@ -70,7 +72,7 @@ namespace ts {
|
|||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
}
|
||||
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
return SemanticMeaning.All;
|
||||
}
|
||||
|
||||
export function getMeaningFromLocation(node: Node): SemanticMeaning {
|
||||
|
@ -78,7 +80,7 @@ namespace ts {
|
|||
return SemanticMeaning.Value;
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.ExportAssignment) {
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
return SemanticMeaning.All;
|
||||
}
|
||||
else if (isInRightSideOfImport(node)) {
|
||||
return getMeaningFromRightHandSideOfImportEquals(node);
|
||||
|
@ -162,10 +164,22 @@ namespace ts {
|
|||
node = node.parent;
|
||||
}
|
||||
|
||||
return node.parent.kind === SyntaxKind.TypeReference ||
|
||||
(node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent)) ||
|
||||
(node.kind === SyntaxKind.ThisKeyword && !isPartOfExpression(node)) ||
|
||||
node.kind === SyntaxKind.ThisType;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ThisKeyword:
|
||||
return !isPartOfExpression(node);
|
||||
case SyntaxKind.ThisType:
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
case SyntaxKind.JSDocTypeReference:
|
||||
return true;
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
return !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isCallExpressionTarget(node: Node): boolean {
|
||||
|
|
16
tests/cases/fourslash/jsdocTypedefTagSemanticMeaning0.ts
Normal file
16
tests/cases/fourslash/jsdocTypedefTagSemanticMeaning0.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
///<reference path="fourslash.ts" />
|
||||
|
||||
// @allowJs: true
|
||||
// @Filename: a.js
|
||||
|
||||
/////** @typedef {number} [|{| "isWriteAccess": true, "isDefinition": true |}T|] */
|
||||
|
||||
////const [|{| "isWriteAccess": true, "isDefinition": true |}T|] = 1;
|
||||
|
||||
/////** @type {[|T|]} */
|
||||
////const n = [|T|];
|
||||
|
||||
const [t0, v0, t1, v1] = test.ranges();
|
||||
|
||||
verify.singleReferenceGroup("type T = number\nconst T: 1", [t0, t1]);
|
||||
verify.singleReferenceGroup("type T = number\nconst T: 1", [v0, v1]);
|
12
tests/cases/fourslash/jsdocTypedefTagSemanticMeaning1.ts
Normal file
12
tests/cases/fourslash/jsdocTypedefTagSemanticMeaning1.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
///<reference path="fourslash.ts" />
|
||||
|
||||
// @allowJs: true
|
||||
// @Filename: a.js
|
||||
|
||||
/////** @typedef {number} */
|
||||
////const [|{| "isWriteAccess": true, "isDefinition": true |}T|] = 1;
|
||||
|
||||
/////** @type {[|T|]} */
|
||||
////const n = [|T|];
|
||||
|
||||
verify.singleReferenceGroup("type T = number\nconst T: 1");
|
28
tests/cases/fourslash/jsdocTypedefTagServices.ts
Normal file
28
tests/cases/fourslash/jsdocTypedefTagServices.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
///<reference path="fourslash.ts" />
|
||||
|
||||
// @allowJs: true
|
||||
// @Filename: a.js
|
||||
|
||||
/////**
|
||||
//// * Doc comment
|
||||
//// * @typedef /*def*/[|{| "isWriteAccess": true, "isDefinition": true |}Product|]
|
||||
//// * @property {string} title
|
||||
//// */
|
||||
|
||||
/////**
|
||||
//// * @type {/*use*/[|Product|]}
|
||||
//// */
|
||||
////const product = null;
|
||||
|
||||
const desc = `type Product = {
|
||||
title: string;
|
||||
}`;
|
||||
|
||||
verify.quickInfoAt("use", desc, "Doc comment");
|
||||
|
||||
verify.goToDefinition("use", "def");
|
||||
|
||||
verify.rangesAreOccurrences();
|
||||
verify.rangesAreDocumentHighlights();
|
||||
verify.singleReferenceGroup(desc);
|
||||
verify.rangesAreRenameLocations();
|
Loading…
Reference in a new issue