From d9afcd915659d945aa0279410bfd5d6ea212b333 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 7 Nov 2021 23:40:55 +0800 Subject: [PATCH 1/6] Add JSDoc's @inheritDoc Support for Static Class Members for TypeScript --- src/services/services.ts | 6 + .../reference/quickInfoJsDocTags7.baseline | 256 ++++++++++++++++++ tests/cases/fourslash/jsDocInheritDoc.ts | 2 +- tests/cases/fourslash/quickInfoJsDocTags7.ts | 47 ++++ 4 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/quickInfoJsDocTags7.baseline create mode 100644 tests/cases/fourslash/quickInfoJsDocTags7.ts diff --git a/src/services/services.ts b/src/services/services.ts index b04052a29c0b6..f50b45ab53784 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -604,6 +604,12 @@ namespace ts { return; } return firstDefined(getAllSuperTypeNodes(classOrInterfaceDeclaration), superTypeNode => { + if (declaration.modifiers?.some(modifier => modifier.kind === SyntaxKind.StaticKeyword)) { + // For nodes like `class Foo extends Bar {}`, superTypeNode refers to an ExpressionWithTypeArguments with its expression being `Bar`. + const baseClassSymbol = checker.getSymbolAtLocation(isExpressionWithTypeArguments(superTypeNode) && !superTypeNode.typeArguments?.length ? superTypeNode.expression : superTypeNode); + const symbol = baseClassSymbol?.exports?.get(declaration.symbol.name as __String); + return symbol ? cb(symbol) : undefined; + } const symbol = checker.getPropertyOfType(checker.getTypeAtLocation(superTypeNode), declaration.symbol.name); return symbol ? cb(symbol) : undefined; }); diff --git a/tests/baselines/reference/quickInfoJsDocTags7.baseline b/tests/baselines/reference/quickInfoJsDocTags7.baseline new file mode 100644 index 0000000000000..ca4d108455197 --- /dev/null +++ b/tests/baselines/reference/quickInfoJsDocTags7.baseline @@ -0,0 +1,256 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags7.ts", + "position": 629, + "name": "1" + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "public,static", + "textSpan": { + "start": 629, + "length": 17 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "doSomethingUseful", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "mySpecificStuff", + "kind": "parameterName" + }, + { + "text": "?", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "tiger", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "lion", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "}", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Useful description always applicable", + "kind": "text" + } + ], + "tags": [ + { + "name": "returns", + "text": [ + { + "text": "Useful description of return value always applicable.", + "kind": "text" + } + ] + }, + { + "name": "inheritDoc" + }, + { + "name": "param", + "text": [ + { + "text": "mySpecificStuff", + "kind": "parameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Description of my specific parameter.", + "kind": "text" + } + ] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags7.ts", + "position": 869, + "name": "2" + }, + "quickInfo": { + "kind": "property", + "kindModifiers": "public,static", + "textSpan": { + "start": 869, + "length": 12 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "someProperty", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Applicable description always.", + "kind": "text" + } + ], + "tags": [ + { + "name": "inheritDoc" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/jsDocInheritDoc.ts b/tests/cases/fourslash/jsDocInheritDoc.ts index 8a19bd0c14e26..a6166d9953f75 100644 --- a/tests/cases/fourslash/jsDocInheritDoc.ts +++ b/tests/cases/fourslash/jsDocInheritDoc.ts @@ -52,6 +52,6 @@ verify.quickInfoAt("1", "constructor Bar(value: number): Bar", undefined); // constructors aren't actually inherited verify.quickInfoAt("2", "(method) Bar.method2(): void", "Foo#method2 documentation"); // use inherited docs only -verify.quickInfoAt("3", "(method) Bar.method1(): void", undefined); // statics aren't actually inherited +verify.quickInfoAt("3", "(method) Bar.method1(): void", 'Foo#method1 documentation'); // use inherited docs too verify.quickInfoAt("4", "(property) Bar.property1: string", "Foo#property1 documentation"); // use inherited docs only verify.quickInfoAt("5", "(property) Bar.property2: object", "Baz#property2 documentation\nBar#property2"); // include local and inherited docs diff --git a/tests/cases/fourslash/quickInfoJsDocTags7.ts b/tests/cases/fourslash/quickInfoJsDocTags7.ts new file mode 100644 index 0000000000000..075f68351eb12 --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocTags7.ts @@ -0,0 +1,47 @@ +/// + +// @noEmit: true +// @allowJs: true + +// @Filename: quickInfoJsDocTags7.ts +////abstract class BaseClass { +//// /** +//// * Useful description always applicable +//// * +//// * @returns {string} Useful description of return value always applicable. +//// */ +//// public static doSomethingUseful(stuff?: any): string { +//// throw new Error('Must be implemented by subclass'); +//// } +//// +//// /** +//// * Applicable description always. +//// */ +//// public static readonly someProperty: string = 'general value'; +////} +//// +//// +//// +//// +////class SubClass extends BaseClass { +//// +//// /** +//// * @inheritDoc +//// * +//// * @param {{ tiger: string; lion: string; }} [mySpecificStuff] Description of my specific parameter. +//// */ +//// public static /*1*/doSomethingUseful(mySpecificStuff?: { tiger: string; lion: string; }): string { +//// let useful = ''; +//// +//// // do something useful to useful +//// +//// return useful; +//// } +//// +//// /** +//// * @inheritDoc +//// */ +//// public static readonly /*2*/someProperty: string = 'specific to this class value' +////} + +verify.baselineQuickInfo(); From b1bf52e88c9a01f95d53d66103cacb31853bb29f Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 27 Mar 2022 15:06:00 +0800 Subject: [PATCH 2/6] use public api --- src/services/services.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index f50b45ab53784..3a7e8ee7abac5 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -605,9 +605,9 @@ namespace ts { } return firstDefined(getAllSuperTypeNodes(classOrInterfaceDeclaration), superTypeNode => { if (declaration.modifiers?.some(modifier => modifier.kind === SyntaxKind.StaticKeyword)) { - // For nodes like `class Foo extends Bar {}`, superTypeNode refers to an ExpressionWithTypeArguments with its expression being `Bar`. - const baseClassSymbol = checker.getSymbolAtLocation(isExpressionWithTypeArguments(superTypeNode) && !superTypeNode.typeArguments?.length ? superTypeNode.expression : superTypeNode); - const symbol = baseClassSymbol?.exports?.get(declaration.symbol.name as __String); + // For nodes like `class Foo extends Bar {}`, superTypeNode refers to an ExpressionWithTypeArguments with its expression being `Bar`. + const baseClassSymbol = checker.getSymbolAtLocation(isExpressionWithTypeArguments(superTypeNode) && !superTypeNode.typeArguments?.length ? superTypeNode.expression : superTypeNode); + const symbol = baseClassSymbol?.declarations?.[0] && checker.getPropertyOfType(checker.getTypeOfSymbolAtLocation(baseClassSymbol, baseClassSymbol.declarations[0]), declaration.symbol.name); return symbol ? cb(symbol) : undefined; } const symbol = checker.getPropertyOfType(checker.getTypeAtLocation(superTypeNode), declaration.symbol.name); From 46fb1c340060a078f835b246602f8654bfaf1db1 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 27 Mar 2022 15:42:48 +0800 Subject: [PATCH 3/6] fix --- src/services/services.ts | 5 +- .../reference/quickInfoInheritDoc.baseline | 256 ++++++++++++++++++ tests/cases/fourslash/quickInfoInheritDoc.ts | 47 ++++ 3 files changed, 305 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/quickInfoInheritDoc.baseline create mode 100644 tests/cases/fourslash/quickInfoInheritDoc.ts diff --git a/src/services/services.ts b/src/services/services.ts index b3df57ebf64f0..090be74e3b7e3 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -643,13 +643,12 @@ namespace ts { } function findBaseOfDeclaration(checker: TypeChecker, declaration: Declaration, cb: (symbol: Symbol) => T[] | undefined): T[] | undefined { - if (hasStaticModifier(declaration)) return; - const classOrInterfaceDeclaration = declaration.parent?.kind === SyntaxKind.Constructor ? declaration.parent.parent : declaration.parent; if (!classOrInterfaceDeclaration) return; + const isStaticMember = hasStaticModifier(declaration); return firstDefined(getAllSuperTypeNodes(classOrInterfaceDeclaration), superTypeNode => { - if (declaration.modifiers?.some(modifier => modifier.kind === SyntaxKind.StaticKeyword)) { + if (isStaticMember) { // For nodes like `class Foo extends Bar {}`, superTypeNode refers to an ExpressionWithTypeArguments with its expression being `Bar`. const baseClassSymbol = checker.getSymbolAtLocation(isExpressionWithTypeArguments(superTypeNode) && !superTypeNode.typeArguments?.length ? superTypeNode.expression : superTypeNode); const symbol = baseClassSymbol?.declarations?.[0] && checker.getPropertyOfType(checker.getTypeOfSymbolAtLocation(baseClassSymbol, baseClassSymbol.declarations[0]), declaration.symbol.name); diff --git a/tests/baselines/reference/quickInfoInheritDoc.baseline b/tests/baselines/reference/quickInfoInheritDoc.baseline new file mode 100644 index 0000000000000..ca4d108455197 --- /dev/null +++ b/tests/baselines/reference/quickInfoInheritDoc.baseline @@ -0,0 +1,256 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags7.ts", + "position": 629, + "name": "1" + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "public,static", + "textSpan": { + "start": 629, + "length": 17 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "doSomethingUseful", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "mySpecificStuff", + "kind": "parameterName" + }, + { + "text": "?", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "tiger", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "lion", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "}", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Useful description always applicable", + "kind": "text" + } + ], + "tags": [ + { + "name": "returns", + "text": [ + { + "text": "Useful description of return value always applicable.", + "kind": "text" + } + ] + }, + { + "name": "inheritDoc" + }, + { + "name": "param", + "text": [ + { + "text": "mySpecificStuff", + "kind": "parameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Description of my specific parameter.", + "kind": "text" + } + ] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags7.ts", + "position": 869, + "name": "2" + }, + "quickInfo": { + "kind": "property", + "kindModifiers": "public,static", + "textSpan": { + "start": 869, + "length": 12 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "someProperty", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Applicable description always.", + "kind": "text" + } + ], + "tags": [ + { + "name": "inheritDoc" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoInheritDoc.ts b/tests/cases/fourslash/quickInfoInheritDoc.ts new file mode 100644 index 0000000000000..d56e1795b773f --- /dev/null +++ b/tests/cases/fourslash/quickInfoInheritDoc.ts @@ -0,0 +1,47 @@ +/// + +// @noEmit: true +// @allowJs: true + +// @Filename: quickInfoJsDocTags7.ts +////abstract class BaseClass { +//// /** +//// * Useful description always applicable +//// * +//// * @returns {string} Useful description of return value always applicable. +//// */ +//// public static doSomethingUseful(stuff?: any): string { +//// throw new Error('Must be implemented by subclass'); +//// } +//// +//// /** +//// * Applicable description always. +//// */ +//// public static readonly someProperty: string = 'general value'; +////} +//// +//// +//// +//// +////class SubClass extends BaseClass { +//// +//// /** +//// * @inheritDoc +//// * +//// * @param {{ tiger: string; lion: string; }} [mySpecificStuff] Description of my specific parameter. +//// */ +//// public static /*1*/doSomethingUseful(mySpecificStuff?: { tiger: string; lion: string; }): string { +//// let useful = ''; +//// +//// // do something useful to useful +//// +//// return useful; +//// } +//// +//// /** +//// * @inheritDoc +//// */ +//// public static readonly /*2*/someProperty: string = 'specific to this class value' +////} + +verify.baselineQuickInfo(); \ No newline at end of file From 4d25f7fc1a7cf323e1954f453019bf430405f495 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sun, 27 Mar 2022 22:48:31 +0800 Subject: [PATCH 4/6] add tests --- .../reference/quickInfoInheritDoc.baseline | 172 +++++++++++++++++- tests/cases/fourslash/quickInfoInheritDoc.ts | 22 ++- 2 files changed, 185 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/quickInfoInheritDoc.baseline b/tests/baselines/reference/quickInfoInheritDoc.baseline index ca4d108455197..0d6d228656df0 100644 --- a/tests/baselines/reference/quickInfoInheritDoc.baseline +++ b/tests/baselines/reference/quickInfoInheritDoc.baseline @@ -1,15 +1,15 @@ [ { "marker": { - "fileName": "/tests/cases/fourslash/quickInfoJsDocTags7.ts", - "position": 629, + "fileName": "/tests/cases/fourslash/quickInfoInheritDoc.ts", + "position": 817, "name": "1" }, "quickInfo": { "kind": "method", "kindModifiers": "public,static", "textSpan": { - "start": 629, + "start": 817, "length": 17 }, "displayParts": [ @@ -187,15 +187,159 @@ }, { "marker": { - "fileName": "/tests/cases/fourslash/quickInfoJsDocTags7.ts", - "position": 869, + "fileName": "/tests/cases/fourslash/quickInfoInheritDoc.ts", + "position": 1143, "name": "2" }, + "quickInfo": { + "kind": "method", + "kindModifiers": "public,static", + "textSpan": { + "start": 1143, + "length": 5 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "func1", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "stuff1", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "BaseClass.func1", + "kind": "text" + } + ], + "tags": [ + { + "name": "param", + "text": [ + { + "text": "stuff1", + "kind": "parameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "BaseClass.func1.stuff1", + "kind": "text" + } + ] + }, + { + "name": "returns", + "text": [ + { + "text": "BaseClass.func1.returns", + "kind": "text" + } + ] + }, + { + "name": "inheritDoc" + }, + { + "name": "param", + "text": [ + { + "text": "stuff1", + "kind": "parameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass.func1.stuff1", + "kind": "text" + } + ] + }, + { + "name": "returns", + "text": [ + { + "text": "SubClass.func1.returns", + "kind": "text" + } + ] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoInheritDoc.ts", + "position": 1282, + "name": "3" + }, "quickInfo": { "kind": "property", "kindModifiers": "public,static", "textSpan": { - "start": 869, + "start": 1282, "length": 12 }, "displayParts": [ @@ -244,11 +388,25 @@ { "text": "Applicable description always.", "kind": "text" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "text over tag", + "kind": "text" } ], "tags": [ { - "name": "inheritDoc" + "name": "inheritDoc", + "text": [ + { + "text": "text after tag", + "kind": "text" + } + ] } ] } diff --git a/tests/cases/fourslash/quickInfoInheritDoc.ts b/tests/cases/fourslash/quickInfoInheritDoc.ts index d56e1795b773f..a08f60e98aba7 100644 --- a/tests/cases/fourslash/quickInfoInheritDoc.ts +++ b/tests/cases/fourslash/quickInfoInheritDoc.ts @@ -3,7 +3,7 @@ // @noEmit: true // @allowJs: true -// @Filename: quickInfoJsDocTags7.ts +// @Filename: quickInfoInheritDoc.ts ////abstract class BaseClass { //// /** //// * Useful description always applicable @@ -15,6 +15,14 @@ //// } //// //// /** +//// * BaseClass.func1 +//// * @param {any} stuff1 BaseClass.func1.stuff1 +//// * @returns {void} BaseClass.func1.returns +//// */ +//// public static func1(stuff1: any): void { +//// } +//// +//// /** //// * Applicable description always. //// */ //// public static readonly someProperty: string = 'general value'; @@ -40,8 +48,18 @@ //// //// /** //// * @inheritDoc +//// * @param {any} stuff1 SubClass.func1.stuff1 +//// * @returns {void} SubClass.func1.returns +//// */ +//// public static /*2*/func1(stuff1: any): void { +//// } +//// +//// /** +//// * text over tag +//// * @inheritDoc +//// * text after tag //// */ -//// public static readonly /*2*/someProperty: string = 'specific to this class value' +//// public static readonly /*3*/someProperty: string = 'specific to this class value' ////} verify.baselineQuickInfo(); \ No newline at end of file From 53ec474d88d4dcce74fb80bf299f3ca8a806ab74 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Fri, 1 Apr 2022 00:33:58 +0800 Subject: [PATCH 5/6] simplify implementation --- src/services/services.ts | 11 +-- .../reference/quickInfoInheritDoc2.baseline | 88 +++++++++++++++++++ .../reference/quickInfoInheritDoc3.baseline | 76 ++++++++++++++++ tests/cases/fourslash/quickInfoInheritDoc2.ts | 22 +++++ tests/cases/fourslash/quickInfoInheritDoc3.ts | 23 +++++ 5 files changed, 213 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/quickInfoInheritDoc2.baseline create mode 100644 tests/baselines/reference/quickInfoInheritDoc3.baseline create mode 100644 tests/cases/fourslash/quickInfoInheritDoc2.ts create mode 100644 tests/cases/fourslash/quickInfoInheritDoc3.ts diff --git a/src/services/services.ts b/src/services/services.ts index 090be74e3b7e3..acf8d3a8e7745 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -648,13 +648,10 @@ namespace ts { const isStaticMember = hasStaticModifier(declaration); return firstDefined(getAllSuperTypeNodes(classOrInterfaceDeclaration), superTypeNode => { - if (isStaticMember) { - // For nodes like `class Foo extends Bar {}`, superTypeNode refers to an ExpressionWithTypeArguments with its expression being `Bar`. - const baseClassSymbol = checker.getSymbolAtLocation(isExpressionWithTypeArguments(superTypeNode) && !superTypeNode.typeArguments?.length ? superTypeNode.expression : superTypeNode); - const symbol = baseClassSymbol?.declarations?.[0] && checker.getPropertyOfType(checker.getTypeOfSymbolAtLocation(baseClassSymbol, baseClassSymbol.declarations[0]), declaration.symbol.name); - return symbol ? cb(symbol) : undefined; - } - const symbol = checker.getPropertyOfType(checker.getTypeAtLocation(superTypeNode), declaration.symbol.name); + const baseType = checker.getTypeAtLocation(superTypeNode); + const symbol = isStaticMember + ? find(checker.getExportsOfModule(baseType.symbol), s => s.escapedName === declaration.symbol.name) + : checker.getPropertyOfType(baseType, declaration.symbol.name); return symbol ? cb(symbol) : undefined; }); } diff --git a/tests/baselines/reference/quickInfoInheritDoc2.baseline b/tests/baselines/reference/quickInfoInheritDoc2.baseline new file mode 100644 index 0000000000000..5d964ae0a9ff9 --- /dev/null +++ b/tests/baselines/reference/quickInfoInheritDoc2.baseline @@ -0,0 +1,88 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoInheritDoc2.ts", + "position": 173, + "name": "1" + }, + "quickInfo": { + "kind": "property", + "kindModifiers": "", + "textSpan": { + "start": 173, + "length": 4 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass", + "kind": "className" + }, + { + "text": "<", + "kind": "punctuation" + }, + { + "text": "T", + "kind": "typeParameterName" + }, + { + "text": ">", + "kind": "punctuation" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "prop", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "T", + "kind": "typeParameterName" + } + ], + "documentation": [ + { + "text": "Base.prop", + "kind": "text" + } + ], + "tags": [ + { + "name": "inheritdoc", + "text": [ + { + "text": "SubClass.prop", + "kind": "text" + } + ] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/quickInfoInheritDoc3.baseline b/tests/baselines/reference/quickInfoInheritDoc3.baseline new file mode 100644 index 0000000000000..1bd0e90a43a3f --- /dev/null +++ b/tests/baselines/reference/quickInfoInheritDoc3.baseline @@ -0,0 +1,76 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoInheritDoc3.ts", + "position": 237, + "name": "1" + }, + "quickInfo": { + "kind": "property", + "kindModifiers": "", + "textSpan": { + "start": 237, + "length": 4 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "SubClass", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "prop", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Base.prop", + "kind": "text" + } + ], + "tags": [ + { + "name": "inheritdoc", + "text": [ + { + "text": "SubClass.prop", + "kind": "text" + } + ] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoInheritDoc2.ts b/tests/cases/fourslash/quickInfoInheritDoc2.ts new file mode 100644 index 0000000000000..8410c4dddf43a --- /dev/null +++ b/tests/cases/fourslash/quickInfoInheritDoc2.ts @@ -0,0 +1,22 @@ +/// + +// @noEmit: true +// @allowJs: true + +// @Filename: quickInfoInheritDoc2.ts +////class Base { +//// /** +//// * Base.prop +//// */ +//// prop: T | undefined; +////} +//// +////class SubClass extends Base { +//// /** +//// * @inheritdoc +//// * SubClass.prop +//// */ +//// /*1*/prop: T | undefined; +////} + +verify.baselineQuickInfo(); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoInheritDoc3.ts b/tests/cases/fourslash/quickInfoInheritDoc3.ts new file mode 100644 index 0000000000000..82ebe5399dbc8 --- /dev/null +++ b/tests/cases/fourslash/quickInfoInheritDoc3.ts @@ -0,0 +1,23 @@ +/// + +// @noEmit: true +// @allowJs: true + +// @Filename: quickInfoInheritDoc3.ts +////function getBaseClass() { +//// return class Base { +//// /** +//// * Base.prop +//// */ +//// prop: string | undefined; +//// } +////} +////class SubClass extends getBaseClass() { +//// /** +//// * @inheritdoc +//// * SubClass.prop +//// */ +//// /*1*/prop: string | undefined; +////} + +verify.baselineQuickInfo(); \ No newline at end of file From dfea5d0e4af2e6ad95942d0ef0adae7a43c84b8d Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sat, 16 Apr 2022 17:27:11 +0800 Subject: [PATCH 6/6] extract comments from inherticDoc --- src/services/jsDoc.ts | 8 ++++++-- src/services/services.ts | 2 +- tests/baselines/reference/quickInfoInheritDoc.baseline | 4 ++++ tests/baselines/reference/quickInfoInheritDoc2.baseline | 8 ++++++++ tests/baselines/reference/quickInfoInheritDoc3.baseline | 8 ++++++++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 43600422a5c84..5bbd7833fad05 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -93,11 +93,12 @@ namespace ts.JsDoc { const parts: SymbolDisplayPart[][] = []; forEachUnique(declarations, declaration => { for (const jsdoc of getCommentHavingNodes(declaration)) { + const inheritDoc = isJSDoc(jsdoc) && jsdoc.tags && find(jsdoc.tags, t => t.kind === SyntaxKind.JSDocTag && (t.tagName.escapedText === "inheritDoc" || t.tagName.escapedText === "inheritdoc")); // skip comments containing @typedefs since they're not associated with particular declarations // Exceptions: // - @typedefs are themselves declarations with associated comments // - @param or @return indicate that the author thinks of it as a 'local' @typedef that's part of the function documentation - if (jsdoc.comment === undefined + if (jsdoc.comment === undefined && !inheritDoc || isJSDoc(jsdoc) && declaration.kind !== SyntaxKind.JSDocTypedefTag && declaration.kind !== SyntaxKind.JSDocCallbackTag && jsdoc.tags @@ -105,7 +106,10 @@ namespace ts.JsDoc { && !jsdoc.tags.some(t => t.kind === SyntaxKind.JSDocParameterTag || t.kind === SyntaxKind.JSDocReturnTag)) { continue; } - const newparts = getDisplayPartsFromComment(jsdoc.comment, checker); + let newparts = jsdoc.comment ? getDisplayPartsFromComment(jsdoc.comment, checker) : []; + if (inheritDoc && inheritDoc.comment) { + newparts = newparts.concat(getDisplayPartsFromComment(inheritDoc.comment, checker)); + } if (!contains(parts, newparts, isIdenticalListOfDisplayParts)) { parts.push(newparts); } diff --git a/src/services/services.ts b/src/services/services.ts index acf8d3a8e7745..dd6025bd67622 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -592,7 +592,7 @@ namespace ts { * @returns `true` if `node` has a JSDoc "inheritDoc" tag on it, otherwise `false`. */ function hasJSDocInheritDocTag(node: Node) { - return getJSDocTags(node).some(tag => tag.tagName.text === "inheritDoc"); + return getJSDocTags(node).some(tag => tag.tagName.text === "inheritDoc" || tag.tagName.text === "inheritdoc"); } function getJsDocTagsOfDeclarations(declarations: Declaration[] | undefined, checker: TypeChecker | undefined): JSDocTagInfo[] { diff --git a/tests/baselines/reference/quickInfoInheritDoc.baseline b/tests/baselines/reference/quickInfoInheritDoc.baseline index 0d6d228656df0..29cc8e3544d6e 100644 --- a/tests/baselines/reference/quickInfoInheritDoc.baseline +++ b/tests/baselines/reference/quickInfoInheritDoc.baseline @@ -396,6 +396,10 @@ { "text": "text over tag", "kind": "text" + }, + { + "text": "text after tag", + "kind": "text" } ], "tags": [ diff --git a/tests/baselines/reference/quickInfoInheritDoc2.baseline b/tests/baselines/reference/quickInfoInheritDoc2.baseline index 5d964ae0a9ff9..40dc3368efd21 100644 --- a/tests/baselines/reference/quickInfoInheritDoc2.baseline +++ b/tests/baselines/reference/quickInfoInheritDoc2.baseline @@ -70,6 +70,14 @@ { "text": "Base.prop", "kind": "text" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "SubClass.prop", + "kind": "text" } ], "tags": [ diff --git a/tests/baselines/reference/quickInfoInheritDoc3.baseline b/tests/baselines/reference/quickInfoInheritDoc3.baseline index 1bd0e90a43a3f..5729c6c1ccfde 100644 --- a/tests/baselines/reference/quickInfoInheritDoc3.baseline +++ b/tests/baselines/reference/quickInfoInheritDoc3.baseline @@ -58,6 +58,14 @@ { "text": "Base.prop", "kind": "text" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "SubClass.prop", + "kind": "text" } ], "tags": [