-
Notifications
You must be signed in to change notification settings - Fork 720
JSDoc completions #1561
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
JSDoc completions #1561
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR ports JSDoc completions functionality from the TypeScript compiler to provide auto-completion support for JSDoc comments and tags. The main purpose is to enable IntelliSense-like features when writing JSDoc documentation, including completion for tag names, parameter names, and type expressions.
Key changes include:
- Implementation of JSDoc completion logic with support for tag names, parameters, and type expressions
- Bug fixes to AST navigation for JSDoc single comment nodes to match TypeScript's behavior
- Updates to string completion handling to return the original item instead of null for better fallback behavior
Reviewed Changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
internal/ls/completions.go |
Core JSDoc completion implementation with new completion data types and handlers |
internal/astnav/tokens.go |
Enhanced VisitEachChildAndJSDoc with proper JSDoc single comment node handling |
internal/ast/utilities.go |
New utility functions for JSDoc node identification and comment handling |
internal/ast/ast.go |
Added AST node methods for accessing JSDoc properties and type expressions |
internal/lsutil/children.go |
Updated to use new VisitEachChildAndJSDoc signature |
internal/ls/utilities.go |
Updated visitor usage and added reparsed node flag check |
internal/ls/string_completions.go |
Changed to return original item instead of nil for better fallback |
internal/format/util.go |
Exported GetLineStartPositionForPosition function |
internal/checker/printer.go |
Added TypeToTypeNode method for type-to-AST conversion |
Test files | Enabled previously skipped JSDoc completion tests and added new basic test |
Is there a chance we need the old non-special case way? |
internal/ast/utilities.go
Outdated
} | ||
|
||
// True if node is of a JSDoc kind that may contain comment text. | ||
// NOTE: while this is a faithful port of Strada's implementation, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should Comments just check this and return nil or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not the way it used to work in Strada. You can access .comments
on the node even if .comments
is a string, but you wouldn't visit them in Strada's forEachChild
.
Is there a chance we need the old non-special case way?
I think we might need a version that visits all things. e.g. in Corsa,ForEachChild
is used in the binder and so probably needs to visit single comments in JSDoc. We may be able to get away with adding the special behavior to Corsa's VisitEachChild
, but I'm not sure.
@sandersn might have a better idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stupid suggestion because I just started thinking about this, but: JSDocTypeLiteral and JSDocTypeSignature are the bodies of a typedef
and a callback
respectively. The reason they don't have comments is that their parent has the comment instead. Why do they return true for this function? Is it because they contain children that could themselves contain comments?
This function is only called in two places, neither of which appear to deal with jsdoc comment text, but finding nodes from a position. My best guess is that the function needs to be renamed so that people don't think Comment() is safe to call on nodes it returns true from.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I got totally confused in my original answer, I thought Jake's comment was about a different function. But I agree we should rename it, I just don't know to what. In Strada this function even has a comment saying "True if node is of a kind that may contain comment text", so maybe this function was wrong in Strada too? I'm not sure. Maybe we should rename to DoNotUseVeryBadIsJSDocCommentContainingNode
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed it and also moved it to the astnav package, the only place where it's used. Couldn't think of a good name because I don't really know what the function is supposed to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor suggestions and questions, but I think this is a faithful port of some slightly odd code.
internal/ast/utilities.go
Outdated
} | ||
|
||
// True if node is of a JSDoc kind that may contain comment text. | ||
// NOTE: while this is a faithful port of Strada's implementation, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stupid suggestion because I just started thinking about this, but: JSDocTypeLiteral and JSDocTypeSignature are the bodies of a typedef
and a callback
respectively. The reason they don't have comments is that their parent has the comment instead. Why do they return true for this function? Is it because they contain children that could themselves contain comments?
This function is only called in two places, neither of which appear to deal with jsdoc comment text, but finding nodes from a position. My best guess is that the function needs to be renamed so that people don't think Comment() is safe to call on nodes it returns true from.
func TestJsdocExtendsTagCompletion(t *testing.T) { | ||
t.Parallel() | ||
t.Skip() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the original test is weird -- we expect to have A
as a completion for A extends /**/
? I guess it's the only class around, but it still doesn't make sense.
internal/ls/completions.go
Outdated
t := "*" | ||
if isObject { | ||
// !!! Debug.assert(!dotDotDotToken, `Cannot annotate a rest parameter with type 'Object'.`); | ||
t = "Object" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait, what? Object
is somehow the worst type for anything in either TS or JS. In Strada it means any
if strict is off but in Corsa it always means Object
. I think object
makes more sense, or *
or any
if object
is nonsensical somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I can't really remember why I used Object
here originally. Maybe that's what I more commonly saw in JS at the time? Anyways, I'll update to object
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait wait. I remembered. It's probably because it's preparing to emit nested object literal type notation:
/**
* @param {Object} param0
* @param {string} param0.a
* @param {number} param0.b
*/
function f({ a, b }) {}
So it might make sense.
It's also one more reason why Object -> any
in Strada, even though I hate that and it's not coming back for Corsa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, this works with object
too:
So keeping object
until someone complains.
Co-authored-by: Nathan Shively-Sanders <[email protected]>
Co-authored-by: Nathan Shively-Sanders <[email protected]>
Port of JSDoc completions. I think nothing had to be changed compared to Strada.
This also includes some bug fixes, mainly a change to
astnav.VisitEachChildAndJSDoc
so that it takes visiting functions and wraps them in a way that skips visiting a JSDoc node's comments when that node has a single comment.The reason for this is that in Strada, that single JSDoc comment used to be encoded as a
comment
property of typestring
, and duringforEachChild
we would not visit that string (because we can only visit nodes), and that also meant thatnode.getChildren()
would not include that single JSDoc comment.In Corsa, every comment is a node. There is no comment property of type string, so we can in fact visit that single comment. But code in services relies on that special behavior, so I added it directly to
astnav.VisitEachChildAndJSDoc
(in the right way this time, instead of the partial fix I had in place before).