Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @callback jsdoc tag #9546

Closed
wants to merge 4 commits into from

Conversation

zhengbli
Copy link
Contributor

@zhengbli zhengbli commented Jul 7, 2016

Fixes #7515

Example:

/**
  * @callback FooHandler
  * @param {string} [eventName]
  * @param eventName2 {number | string}
  * @param eventName3
  */

is the equivalent of TypeScript code:

type FooHandler = (eventName?: string, eventName2: number | string, eventName3: any) => void;

Types defined by @callback tag are block scoped, and will show up in the navigate to list.

@zhengbli zhengbli changed the title Add callback jsdoc tag Add @callback jsdoc tag Jul 7, 2016
@@ -264,6 +265,14 @@ namespace ts {
let functionType = <JSDocFunctionType>node.parent;
let index = indexOf(functionType.parameters, node);
return "p" + index;
case SyntaxKind.JSDocParameterTag:
Debug.assert(node.parent.kind === SyntaxKind.JSDocCallbackType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a message to the assert here and above? There's not much distinguishing a stack trace from a user otherwise.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 13, 2016

@zhengbli please check with @sandersn on this PR and #10671

}

export interface JSDocCallbackType extends JSDocType, Declaration {
parameterTags?: NodeArray<JSDocParameterTag>;
Copy link
Member

@sandersn sandersn Sep 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use @return here in @callback too, right? I think JSDocCallbackType should have a field for that too.

@@ -221,7 +221,7 @@ namespace ts.NavigationBar {
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.kind === SyntaxKind.JSDocTypedefTag) {
if (tag.kind === SyntaxKind.JSDocTypedefTag || tag.kind === SyntaxKind.JSDocCallbackTag) {
Copy link
Member

@sandersn sandersn Sep 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the fourslash rename test cover this? I didn't think that it does

// This is different kind of jsDoc parameter tag. Here is the address the case when
// the parameter declaration is a normal one (in the code), while the type of the
// parameter may be found in the jsdoc comment.
const paramTag = getCorrespondingJSDocParameterTag(node);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to simplify this code and have getCorrespondingJSDocParameterTag return a paramTag that works whether or not node is aParameterDeclaration or a JSDocParameterTag.

@@ -1528,6 +1530,15 @@ namespace ts {
jsDocTypeLiteral?: JSDocTypeLiteral;
}

export interface JSDocCallbackTag extends JSDocTag, Declaration {
name: Identifier;
type: JSDocCallbackType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually quite a bit like an @typedef in that they both support multiple lines and both produce a single type (declaration?). Could the interfaces be shared?

Maybe even some of the machinery could be shared. I haven't read that part yet.

@@ -2397,7 +2408,7 @@ namespace ts {
}

export interface Signature {
declaration: SignatureDeclaration; // Originating declaration
declaration: SignatureDeclaration | JSDocCallbackType; // Originating declaration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why doesn't @typedef need to be special-cased onto a declaration like this? At least I don't find an example of it happening.

@@ -3178,6 +3178,14 @@ namespace ts {
type = checkExpressionCached((<BinaryExpression>declaration.parent).right);
}
}
else if (declaration.kind == SyntaxKind.JSDocParameterTag) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when can this happen?

if (minArgumentCount < 0) {
minArgumentCount = i - (hasThisParameter ? 1 : 0);
if (isJSDocParameterTag(param)) {
if (isJSDocOptionalParameterTag(param)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is really almost the same as the code in the !isJSDocParameterTag branch. I think you should get rid of the true branch and find a way to make the already-existing isJSDocOptionalParameterTag (currently, line 4545) check work.

@@ -4474,7 +4496,7 @@ namespace ts {
}
}

function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
function getSignatureFromDeclaration(declaration: SignatureDeclaration | JSDocCallbackType): Signature {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have to getSignatureFromDeclaration for JSDocCallbackType and not for JSDocTypedefTag?

case SyntaxKind.AtToken:
if (canParseTag) {
parentTagTerminated = !tryParseChildTag(parentTag);
if (!parentTagTerminated) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you invert parentTagTerminated and rename it? I'd much rather read parentTagContinues = tryParseChild(parentTag); if (parentTagContinues) resumePos = scanner.getStartPos();


//// /**
//// * @callback FooHandler
//// * @param {string} eventName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add some documentation? I don't think it'll work now, but once my jsdoc change is also merged, then parseParamTag should also parse documentation. In the meantime, it should be skipped correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the parser in services almost certainly won't skip it or retrieve it correctly. I don't think that's a problem.

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parser changes look good but the binder looks like it could be simplified and I don't understand why some parts of the checker need to change, like getDeclarationFromSignature.

@sandersn
Copy link
Member

We should discuss which order to merge this vs #10671. As far as I can tell, they shouldn't overlap very much except

  1. a few additions of skipWhitespace() in the parser (since Remove service's jsdoc parser and enhance parser's jsdoc parser #10671 makes jsdoc now track whitespace)
  2. doc comments on callback parameters need to work, which means more tests and possibly some code changes too.

@mhegazy
Copy link
Contributor

mhegazy commented Dec 30, 2016

@zhengbli can you update his change?

@mhegazy
Copy link
Contributor

mhegazy commented Apr 20, 2017

closing this for now.

@mhegazy mhegazy closed this Apr 20, 2017
@sandersn
Copy link
Member

Note that once @typedef supports generics via @template, @callback should too.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants