GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — inferFromUsage codefix now emits JSDoc in JS files by sandersn · Pull Request #27610 · microsoft/TypeScript Conversation
Member
inferFromUsage now emits JSDoc. It emits For example: function f(x) { } f(1) Gives the annotation /** * @param {number} x */ Currently, only Note that I generate strings for JSDoc. I don't know enough about printing trivia to know whether it's worthwhile to generate a structure and then print it.
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
Probably everything else, but not as well. It's still bad, but at least it's not wrong. Also, remove redundant is(Set|Get)Accessor functions. By surrounding the parameter name with brackets. It is super, super ugly right now. More to come, though. annotate is only called in annotateVariableDeclaration where we don't know whether we're in JS or not.
Nathan Shively-Sanders (sandersn)
requested review from
a user and
Andrew Casey (amcasey)
|
|
|
||
| // @allowJs: true | ||
| // @checkJs: true | ||
| // @noImplicitAny: true |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think this shouldn't be necessary because we should make it a suggestion diagnostic anyway, if we don't already.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Either noImplicitAny isn't triggered as a suggestion (perhaps just for JS?), or the test is only seeing codefixes from errors. Any idea which? I don't know how to tell what gets triggered as a suggestion, do you?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
verify.getSuggestionDiagnostics should allow you to test that.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
There is one suggestion, but it's "'numeric' is declared but its value is never read.". I don't think noImplicitAny is leading to a infer-from-usage suggestion. It probably should someday, but I'd like to improve the precision and recall of it first.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Filed #27624
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| verify.fileAfterCodeFix( | ||
| `/** @param {number} a */ | ||
| /** |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Could you file this as a bug? We should combine comments, not add a new one.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'll file it after merging the PR since the bug depends on being able to emit JSDoc in the first place.
I personally think the current approach is a good compromise and that we'll rarely have to generate partial JSDoc. The bigger lack is untyped JSDoc:
/** @param x Just a description, no type */ function f(x) { return x * x }
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| newFileContent: | ||
| `export class C { | ||
| /** | ||
| * @param {Promise<typeof import("/a")>} val |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think this should be "./a" when we synthesize the import. Doesn't look so bad in this example but in a real example this might import from /home/myname/projects/typescript/someproject/src/components/a.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Wow, nice catch. I asked Wesley and he said that getTypeNodeIfAccessible needs to provide more members to the symbol tracker host (from Program and LanguageServiceHost). It sounds fairly simple, but it's not related to this change, so I'll put it in a separate PR.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Fixes #20113 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
|
|
||
| public tryInsertJSDocType(sourceFile: SourceFile, node: Node, type: TypeNode): void { | ||
| const printed = createPrinter().printNode(EmitHint.Unspecified, type, sourceFile); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Think this could also use getNonformattedText.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
|
||
| public insertCommentThenNewline(sourceFile: SourceFile, character: number, position: number, commentText: string): void { | ||
| const token = getTouchingToken(sourceFile, position); | ||
| const text = "/**" + commentText + "*/" + this.newLineCharacter + repeatString(" ", character); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
" " [](start = 91, length = 3)
Will the formatting done after the fix is applied convert this to tabs if that's what the user prefers?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
What we do for other fixes is to copy exactly the indentation string that they were using -- if their preferred indentation is tab, space, space, tab, space, space, we should copy that. I'll make a new PR unifying our comment code since #27565 has its own separate jsdoc-adding helpers.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
There are cases where we need to make to new indentation, though, as in the single-line test case that needs to annotate class C {m(x) {return x;}}
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| commentText += this.printJSDocParameter(indent, printed, declaration.name, isOptional); | ||
| } | ||
| } | ||
| commentText += repeatString(" ", indent + 1); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
commentText += repeatString(" ", indent + 1); [](start = 12, length = 45)
This seems like it belongs in insertCommentThenNewline (which would then either need to check for newlines in the text or accept a flag indicating whether or not the */ should be indented).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| if (isOptionalParameter) { | ||
| printName = `[${printName}]`; | ||
| } | ||
| return repeatString(" ", indent) + ` * @param {${printed}} ${printName}\n`; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
repeatString(" ", indent) [](start = 19, length = 25)
Having these scattered through the code seems fragile. What about passing an array of lines to insertCommentThenNewline and letting it handle breaks and indentation?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| /** Note: output node may be mutated input node. */ | ||
| function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } { | ||
| export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
export [](start = 8, length = 6)
Why is this exported?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| this.insertNodeAt(sourceFile, endNode.end, type, { prefix: ": " }); | ||
| } | ||
|
|
||
| public tryInsertJSDocType(sourceFile: SourceFile, node: Node, type: TypeNode): void { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
tryInsertJSDocType [](start = 15, length = 18)
I feel like I'm missing something obvious, but why "try"?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| changes.tryInsertJSDocParameters(sourceFile, result); | ||
| } | ||
|
|
||
| function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
[](start = 0, length = 3)
This looks unintentional.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| return callContexts && declaration.parameters.map((parameter, parameterIndex) => { | ||
| const types: Type[] = []; | ||
| const isRest = isRestParameter(parameter); | ||
| let isOptional = false; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
isOptional [](start = 20, length = 10)
I don't understand this flag. I'll drop by.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| let isOptional = false; | ||
| for (const callContext of callContexts) { | ||
| if (callContext.argumentTypes.length <= parameterIndex) { | ||
| isOptional = isInJSFile(declaration); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
isInJSFile [](start = 37, length = 10)
I assume isInJSFile is very cheap but I would habitually have either pulled it out of the loop or checked isOptional ||.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.