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

[Salsa] Support @typedef for jsdoc #8103

Merged
merged 21 commits into from
May 31, 2016
Merged

Conversation

zhengbli
Copy link
Contributor

This PR adds support for the @typedef JSDoc tag. The current implementation includes the following formats:

Format 1:

/** @typedef { [Type Expression] } [Type Name] */

Format 2:

/** @typedef  [Type Expression] */
var [Type Name];

Format 3:

/** 
  * @typedef  {Object} [Type Name] 
  * @property {[Type Expression]} [Property Name]
  * @property {[Type Expression]} [Property Name]
  * ...
  */

Format 4:

/** 
  * @typedef  [Type Name] 
  * @type {Object}
  * @property {[Type Expression]} [Property Name]
  * @property {[Type Expression]} [Property Name]
  * ...
  */

Current the "Type name" can only be an identifier. Dotted names (qualified names) support may be added in future updates. The types defined by the @typedef tag are the same as type aliases in TypeScript, and they have scopes as well.

Basic support in language services like go to definition and rename is added as well.

Fixes #6976

@zhengbli
Copy link
Contributor Author

@@ -342,6 +342,9 @@ namespace ts {
JSDocReturnTag,
JSDocTypeTag,
JSDocTemplateTag,
JSDocTypedefTag,
JSDocPropertyTag,
JSDocTypeLiteral,
Copy link
Contributor

Choose a reason for hiding this comment

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

question: what is the JSDocTypeLiteral?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A @typedef is similar to a type alias in TS. In TS you can give an alias to an object type literal:

type T = { name: string, age: number}

which is equivalent to the following jsdoc:

/** 
 * @typedef {Object} T
 * @property {string} name
 * @property {number} age
 */

So I defined the @propertys as JSDocTypeLiteral, as it functions like object type literal, but with a different shape.

@billti
Copy link
Member

billti commented Apr 19, 2016

Adding cross-reference to #7758

@@ -269,20 +275,36 @@ namespace ts {
scanner.setText((sourceFile || this.getSourceFile()).text);
children = [];
let pos = this.pos;
const useJSDocScanner =
Copy link
Contributor

@yuit yuit Apr 19, 2016

Choose a reason for hiding this comment

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

Why don't u check like this : const useJSDocScanner = isJSDoc(this)? actaully question: why you don't use JSDocScanner on other JSDoc that is not listed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

JSDocScanner limits scanning to several allowed characters, while disallowed characters are all scanned as unknown tokens. JSDoc nodes include JSDoc type nodes and JSDoc type expression nodes, and if the current position is inside a JSDoc type expression, it should allow all characters, because that part is essentially the same as code. For example, the < and > are not allowed in JSDoc tokens, they are fine when used with generics in JSDoc type expression.

@RyanCavanaugh
Copy link
Member

What's the perf impact of parsing the extra comments? Any?

if (includeJsDocComment) {
const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode);
for (const jsDocChild of jsDocChildren) {
let start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be "const"

@zhengbli
Copy link
Contributor Author

zhengbli commented Apr 25, 2016

I ran the following sample for the perf bench mark:

The Setup:

In an empty folder, do

npm install ember-cli

(I chose ember-cli because it is big enough to see the bind/parsing time difference)
then create an empty jsconfig.json with the following content:

{
    "exclude": []
}

The bench mark script is:

Write-Output "TSC runs:"
0..10 | % {tsc -p ".\jsconfig.json" --noEmit --diagnostics | Select-String "Bind Time|Parse Time"}

Write-Output "PR runs:"
0..10 | % { node --max-old-space-size=2000 "C:\Users\lizhe\Documents\github\TypeScript\built\local\tsc.js" -p ".\jsconfig.json" --noEmit --diagnostics | Select-String "Bind Time|Parse Time"}

The Result:

perf

… typedefForJsdoc

# Conflicts:
#	src/services/services.ts
@zhengbli
Copy link
Contributor Author

zhengbli commented May 5, 2016

So it turns out that my previous benchmark wasn't very accurate at all, for several reasons:

  1. I was running the tests on a laptop on battery mode, and there are browsers / other background programs running at the same time
  2. The tsc.js for the PR was generated via calling jake local, which by default is in "debug mode", things like const enums are preserved for debugging purpose. I should instead use jake release local to generate the tsc.js.
  3. I used the command line tsc for the baseline, which may have node different parameters with what I used for the PR tests.
  4. On the PR I didn't pull from the latest master, so other changes may have impacted the results as well.

I rerun the benchmarks with these factors fixed. The new environment is:
a) a dedicated Ubuntu machine with no graphic interface (ssh)
b) a fresh installed windows 10 machine that is:

  • not domain-joined
  • no active tasks in task scheduler
  • desktop with high-performance power management options
  • as few background apps as possible (no onedrive, browser etc.)

c) use node for both cases, the new script is:

Param($count)

Write-Output "PR runs:"
0..($count - 1) | % { node --max-old-space-size=2000 "C:\Users\lan\Documents\github\TypeScript\built\local\tsc.js" -p ".\jsconfig.json" --noEmit --diagnostics | Select-String "Bind Time"}

Write-Output "Master runs:"
0..($count - 1) | % { node --max-old-space-size=2000 "C:\Program Files\nodejs\node_modules\typescript\lib\tsc.js" -p ".\jsconfig.json" --noEmit --diagnostics | Select-String "Bind Time" }

d) use the latest nightly and merge the PR with master locally. Checked the diff of two tsc.js files to make sure that the only differences are indeed caused by the PR.

And the benchmark result shows that the PR is not slower in binding time:

a) on the Ubuntu machine:
image
image

b) on the Windows machine:
win
image

@RyanCavanaugh
Copy link
Member

👍 especially if it's somehow faster 😉

… typedefForJsdoc

# Conflicts:
#	src/services/services.ts
#	tests/cases/unittests/jsDocParsing.ts
… typedefForJsdoc

# Conflicts:
#	src/services/utilities.ts
@@ -1510,6 +1516,25 @@ namespace ts {
typeExpression: JSDocTypeExpression;
}

// @kind(SyntaxKind.JSDocTypedefTag)
export interface JSDocTypedefTag extends JSDocTag, Declaration {
name: Identifier;
Copy link
Contributor

Choose a reason for hiding this comment

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

name, typeExpresion and type should be optional.

@mhegazy
Copy link
Contributor

mhegazy commented May 26, 2016

I would add a navigate to test for typedefs

@mhegazy
Copy link
Contributor

mhegazy commented May 31, 2016

👍

@mhegazy
Copy link
Contributor

mhegazy commented May 31, 2016

#8849 touches the same areas, can you merge this in after that ones goes in.

@zhengbli zhengbli merged commit 166f399 into microsoft:master May 31, 2016
@zhengbli zhengbli deleted the typedefForJsdoc branch May 31, 2016 23:24
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
6 participants