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

Editor support for @see and {@link} in JSDoc comments tags #35524

Closed
Elarcis opened this issue Dec 5, 2019 · 25 comments
Closed

Editor support for @see and {@link} in JSDoc comments tags #35524

Elarcis opened this issue Dec 5, 2019 · 25 comments

Comments

@Elarcis
Copy link

@Elarcis Elarcis commented Dec 5, 2019

Search Terms

  • jsdoc @link
  • jsdoc @see
  • jsdoc @see @link

Suggestion

Reminder of what {@link} does

Whenever a {@link} tag is encountered in JSDoc, it’d be nice to have it formatted as an actual anchor. It works with URL and symbols relative to the documented one: a function, a property of the current class, or a function in another class?

The @see tag can also reference a symbol without any {@link}, provided there is only a path to a symbol and no free-form text next to it. https://jsdoc.app/tags-see.html

Use Cases

I often speak of other functions/classes in my doc comments, and as {@link} is described on JSDoc’s website, it’d be nice to have it parsed by the langage server and have it shown as a clickable link in any compatible doc widget.

Examples

import { Blah } from "./blah";

/** 
 * This won’t work due to scope: {@link baz}
 * This will: {@link Foo#baz}
 *
 * 
 * The @see annotation can reference symbols as well, as stated on 
 * {@link https://jsdoc.app/tags-see.html JSDoc’s website}
 * @see Document
 */
class Foo<T> {
  /** I want a link to {@link bar}. */
  private baz: T;
  
  /** But I can also reference {@link Foo | the parent class}. */
  public bar(): T {
    return this.baz;
  }

  /** @deprecated please use {@link Blah#grog} instead.  */
  public grog(): void {}

  /** What to do with this link? {@link mysterySymbol} */
  public grug(): void {}
}

Non-imported symbols

A question that subsists is “what to do when {@link} refers to a symbol that exists in the project but isn’t imported in the current file?”

  • Do nothing? {@link mysterySymbol} is converted into a basic non-interactive mysterySymbol? Con is that to get a working link, you’d have to import symbols that are only used for doc.
  • Allow an import("the-module").mysterySymbol syntax like what was done for types declarations?
  • Do a “best guess” and instead of directly going to a file:line:column, find all references of the given symbol in the project and somehow make the editor open a “peek” widget?
  • What if the symbol is located in a dependency, i.e. node_module?

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@yGuy
Copy link

@yGuy yGuy commented Feb 3, 2020

The JetBrains IDEs have had support for this forever (the see and link syntax is part of the JavaDoc syntax for almost 25 years, now). For me, it's the single most annoying difference in documentation lookup behavior when I switch from WebStorm to VS Code. We even provide a script to our customers so that they can remove all the internal links from the documentation in the d.ts file to make it look less ugly and make it more usable.

WebStorm, too, has the problem of unresolved documentation links when there is no import for the Symbol.

JSDoc actually in a way proposes a solution to this issue with a Syntax to "import" a type from another "module":

/** An event. Its name is module:foo/bar.event:MyEvent.
 * @see module:foo/bar.SomeNonImportedSymbol
 */

Adapted from https://jsdoc.app/about-namepaths.html

@sandersn
Copy link
Member

@sandersn sandersn commented Feb 4, 2020

More issues to consider in the proposal:

  1. what kinds of references should be supported? Typescript doesn't support many jsdoc-style namepaths.
  2. what kinds of urls should be supported? I think parsing urls can get hairy fast.
  3. Should invalid @link tags ever have an error?
  4. how should the parser choose between URL and Symbol references? Should it be a best-guess kind of thing?

Some nice-to-haves that we'll need before any implementation happens:

  1. Data on frequency of @link tags and the frequency of URL-vs-symbol as well as various kinds of namepaths.
  2. Thoughts about effects on incremental parsing. (we're probably OK disabling it here, but we should think about it first.)
  3. Thoughts about efficiency and complexity of jsdoc parsing: @link is the only inline tag and the parser currently doesn't have any infrastructure to support that.

Edit: Playing the role of obstinate implementer, I'd say that we could get 80% or more of the benefit of @see and @link just parsing parsing them as special non-tags of the form @see EntityName, which then add a list of references to whatever tag they're on:

/** @param x - a thing; @see foo.bar for details */

Would produce

{ 
  kind: JSDocParameterTag, 
  name: "x", 
  comment: "a thing; @see foo.bar for details",
  references: ["foo.bar"] // or an actual symbol, after binding is done
}
@sandersn
Copy link
Member

@sandersn sandersn commented Feb 4, 2020

@DanielRosenwasser the previous issue #16498 had 60 upvotes. Let's spend some time thinking again about whether we want this.

@mjbvz
Copy link
Contributor

@mjbvz mjbvz commented Feb 4, 2020

@sandersn Checkout vscode.d.ts for examples of how VS Code handle documentation links today (For example, see: [CodeActionKinds](#CodeActionKind))

It'd be great if we could move away from our homegrown solution that only works inside vscode.d.ts to a more standard one powered by TS

@gangsthub
Copy link

@gangsthub gangsthub commented Feb 5, 2020

Would it be possible that relative paths were also considered (JSDocs' namepaths)? I was thinking that it could be useful for referencing docs or other assets:

// (might not be the exact syntax)
/**
 * Without the `@link`
 * @see ../README.md#Troubleshooting
 */

// or

/**
 * See {@link ./my_asset.svg}...
 */
@JasonHK
Copy link

@JasonHK JasonHK commented Apr 12, 2020

4. how should the parser choose between URL and Symbol references? Should it be a best-guess kind of thing?

@sandersn I think if the string starts with a protocol and not starts with either module:, external: or event:, then it's a URL.

And also, if the string starts with ./ or ../, then it's a relative path. Omitting ./ was not permissible since it will easily be mixed with namepaths.

By the way, should the namepath syntax follow JSDoc, using # for instance members, . for static members and ~ for inner members?

@DanielRosenwasser DanielRosenwasser changed the title TSServer – support for @see and {@link} in JSDoc comments TSServer – support for @see and {@link} in JSDoc comments tags Apr 22, 2020
@DanielRosenwasser DanielRosenwasser changed the title TSServer – support for @see and {@link} in JSDoc comments tags Editor support for @see and {@link} in JSDoc comments tags Apr 22, 2020
@ackvf
Copy link

@ackvf ackvf commented Apr 30, 2020

Our code comments sometimes mention symbols in other files. I want to be able to copy such a path LayoutEditor.tsx@acceptsChildren and paste it into goToSymbol field like this:

vscode-go-to-symbol

However, it doesn't work currently and needs to be done in two steps.

taken from microsoft/vscode#96651

@bradennapier
Copy link

@bradennapier bradennapier commented May 21, 2020

Ahh interesting, I was actually playing with implementing some of these types of things in a PR but I ended up keeping it simple to make sure there was wide support for it. I also wasn't sure what was possible since I haven't played with the tsServer in the past.

I have to see this is definitely huge IMO. The rich realtime documentation as you code is probably the most underrated and powerful features to come with modern programming / IDE's - and making these hovers/popups more rich and useful will be a huge plus!

My PR aims to be more general purpose by allowing absolute project & workspace linking, but it does reach into the tsServer to grab the definition of the type being documented. I believe in the same light there are other commands here that could grab symbols locations and provide those links... I just wasn't sure how to link to file & line numbers but prob can add it (assuming tsServer provides necessary pieces) if the PR gains support!

Note: The current implementation is broken for relative paths. This is due to the fact they are utilizing the open file to resolve the relative link. This is problematic since we often are imported values from other files. The PR here fixes that issue for the hovers.

At the very least it may provide insight into the vscode implementation requirements!

microsoft/vscode#98238

@bradennapier
Copy link

@bradennapier bradennapier commented May 21, 2020

A question that subsists is “what to do when {@link} refers to a symbol that exists in the project but isn’t imported in the current file?”

imo it should not do anything and any links should be required in the file linking them. It simply makes things less magical and makes sense. Auto resolution simply has too many potential caveats and problems that can also be specific to the users environment at some times.

import() is interesting option and should prob be supported as long as the preview text is then transformed to the symbol name automatically which is trivial.


While the jsdoc mentions resolution via something like MyType#my I would say it makes the most sense to just support standard typescript semantics, although the question becomes how to handle the required generics. Foo<any>['baz'] -- perhaps allowing them to be omitted in links and/or just provide the signature Foo<T>['baz'] since this wouldn't affect resolving the location of the definition.

{@link Foo<T>['baz']}
// or just
{@link Foo['baz']}
@leontepe
Copy link

@leontepe leontepe commented Jun 7, 2020

I just read through all of the previous issues and everything and I have to say I'm rather confused. But I would like to contribute here, based on how big of a task that actually is, but I would definitely like to look into it.

Does anybody have a recommendation for where to start and can maybe outline the current status of this whole issue, what has already been done and what still needs to be done?

I agree with @bradennapier, live documentation via popups in VSCode would be a huge plus for developer experience and I personally would love to use it myself.

Also, what role does TSDoc currently play here? I think it shouldn't only support plain JSDoc fully but also TSDoc.

If there is any kind of work group currently looking into this issue specifically I would love to join.

@JasonHK
Copy link

@JasonHK JasonHK commented Jun 10, 2020

I just read through all of the previous issues and everything and I have to say I'm rather confused. But I would like to contribute here, based on how big of a task that actually is, but I would definitely like to look into it.

Does anybody have a recommendation for where to start and can maybe outline the current status of this whole issue, what has already been done and what still needs to be done?

I agree with @bradennapier, live documentation via popups in VSCode would be a huge plus for developer experience and I personally would love to use it myself.

Also, what role does TSDoc currently play here? I think it shouldn't only support plain JSDoc fully but also TSDoc.

If there is any kind of work group currently looking into this issue specifically I would love to join.

@leontepe I think microsoft/tsdoc is just a reference implementation.

@leontepe
Copy link

@leontepe leontepe commented Jun 10, 2020

I think microsoft/tsdoc is just a reference implementation.

As far as I understand it, TSDoc is a documentation standard that's similar to / based on JSDoc but without unnecessary type documentations that are already included in the TypeScript language itself. That's why I was saying it should take that into account as well, IMO.

@JasonHK
Copy link

@JasonHK JasonHK commented Jun 10, 2020

I think microsoft/tsdoc is just a reference implementation.

As far as I understand it, TSDoc is a documentation standard that's similar to / based on JSDoc but without unnecessary type documentations that are already included in the TypeScript language itself. That's why I was saying it should take that into account as well, IMO.

Yeah, I agree.

There are some tags that were supported in TSDoc but not in TypeScript language server, like @link and @remarks tag.

Also, I prefer the way TSDoc handle the @example tag. It always treat it's content as Markdown, even contents that weren't start with ```.

Plus, TSDoc handles @ escape correctly. @tag inside Markdown code block won't treat as TSDoc tag.

@leontepe
Copy link

@leontepe leontepe commented Jun 10, 2020

@JasonHK Yeah. And since basically every piece of code Microsoft writes is moving towards TypeScript and away from plain JavaScript, I think they should also have good native support for TSDoc in VSCode.

@qJake
Copy link

@qJake qJake commented Aug 31, 2020

Are we still blocking on TSServer not producing the correct scope information for {@link Symbol} tags at the point of definition?

Would love to have support for this. I (and many others) can draw parallels to C#'s /// Check out <see cref="MyOtherClass" /> which works beautifully, and is exactly how I would expect {@link Symbol} to work.

Real-world sample from a project I'm working on, where I wanted to link to Array.prototype.sort():

/**
 * Helper function to pass to {@link Array.prototype.sort|Array.sort()} to sort vectors
 */
@sandersn
Copy link
Member

@sandersn sandersn commented Sep 3, 2020

I surveyed @see and @link usage in Definitely Typed, our user tests, and anything in their node_modules.
All together, there were 93,382 uses, about 10,821 after deduping and removing http links, which were about half of the total.
I wrote a very messy script for this -- it's not perfect; lodash still got counted several times for example.
However, 10,000 is a nice round number that makes it easy to visualise usage rates.

@see and @link were both used about the same rate -- each showed up on a little over 60% of lines.

  • How many times was the reference a single identifier? 25% (although my regex may have missed some)
  • How many times was # used? 12%
  • How many times was ~ used? 0.3%
  • That probably means that the other 63%-ish percent were entity names.

Other patterns:

  • How often was @see used as an inline tag? 1%.
  • How often were @see and @link used together, eg @see {@link Foo}? 11%
  • How often was a call expression used to show that the referent was a function, eg @see M.f()? 6%
  • How often was the module: prefix used? 2%
  • How often was markdown-like @see [x.y]{@link a.b} used? 1%
  • How often were backticks used with @see? 1%

Also, for example, lodash uses the module name _ in its references like _.reduce even though I don't think _ is actually defined inside the lodash modules. This may mean that a lot of references turn out not to resolve correctly. I need to come up with a way to estimate how big of a problem this is.

To me, this says that a good 90% implementation is:

  1. Both @see and @link, starting with whichever is easiest.
  2. Normal typescript entity name resolution.
  3. Ability to partially resolve the call expression and # uses.

When both @see and @link are done, the @see {@link x} syntax should definitely be tested to make sure it works.

@sandersn
Copy link
Member

@sandersn sandersn commented Sep 9, 2020

Thanks to @Kingwl, #39760 is now merged -- it's an 80% solution that supports @see with normal typescript entity name resolution. Next steps will be better VS Code integration and @link support.

@mjbvz
Copy link
Contributor

@mjbvz mjbvz commented Sep 9, 2020

Here's my proposal on how TypeScript could return the @see and @link tags to editors in a backwards compatible way (shown for a quickinfo response for clarify):

interface QuickInfoResponseBody {
    ...

    tags: JSDocTagInfo[]; // existing
}

interface JSDocTagInfo {
    name: string; // existing
    text?: string; // existing

    // new
    links?: ReadonlyArray<JSDocLink>
}

interface JSDocLink {
    /**
     * Starting index of the link in the jsdoc text (zero based)
     */
    textStartOffset: number;

    /**
     * Ending index of the link in the jsdoc text (zero based, non-inclusive)
     */
    textEndOffset: number;

    /**
     * Where to navigate to when the user clicks the link
     */
    link: FileSpanWithContext;
}
  • Older clients could safely ignore the new links field

  • We'd want support for jsdoc links in hovers, suggestion details, and parameter hints to start with.

  • Eventually we could also support JSDocLink in places such as QuickInfoResponseBody.documentation

Unfortunately I couldn't find any similar apis in the LSP to base this on. I think we currently just use markdown links in the LSP

@KilianKilmister
Copy link

@KilianKilmister KilianKilmister commented Sep 14, 2020

@sandersn Love the little statistic you posted, I'm a big fan of these kind of data-analysis.


What's the thought on using standard markdown links for symbol referencing?

eg.

/** @see [SomeClass](SomeSymbol) */

and

/**
 * An Object describing the kind of action that should be performed on the
 * lexingState
 * - `push`: add the `target` state to the stack
 * - `swap`: change to `target` state, replacing the top of the stack
 * - `pop`: change to the previous state in the stack. (this option ignores
 * any `target` state)
 * some inline text (@see [SomeClass]) some more text
 *
 * [SomeClass]:<SomeSymbol>
 */

Would be neat to have that at some point.

VS code (and i'm assuming the other popular editors aswell) do format and (try) to resolve normal markdown links in comments already, but only works for proper URLs and Paths right now of course.

JSDoc style [link text]{@link namepathOrURL} would work fine too, of course. Just wondering if this is something that's in consideration. It would make it a bit easier for people who write/read a lot of raw markdown but aren't too familiar with JSDoc style

@yGuy
Copy link

@yGuy yGuy commented Sep 15, 2020

I'd rather keep the number of different syntaxes for the same purpose as low as possible. The more we are going to support the higher the cost will be for future and alternative IDE vendors to support all the different syntaxes and the lower the chances are that code that you write today will work great with future/alternative IDEs.

I see that one can always can come up with syntax that may look more convenient to write, however I prefer to see a widely accepted and implemented standard that works across editors and tools.

E.g. the JetBrains IDEs have been supporting the "javadoc-like/jsdoc" syntax for years and hence there is quite a big number of libraries that use this documentation format. If we come up with yet "another standard" the worst thing that could happen is that neither of the variants is supported in both big editors and it gets nearly impossible for library authors to write documentation files that work in both IDEs.

@qJake
Copy link

@qJake qJake commented Sep 16, 2020

I second @yGuy - if I'm a new developer and I want to write JS/TS documentation, I'm going to search for how to do that and I'm going to get results about (long-standing, widely accepted) JSDoc documentation. So, my opinion would be that we stick as close to the JSDoc spec as possible, for backwards compatibility and continuity reasons.

@KilianKilmister
Copy link

@KilianKilmister KilianKilmister commented Sep 16, 2020

@yGuy I don't mind not having this option, and you do make some good points.
I was just curious.

@NoelAbrahams
Copy link

@NoelAbrahams NoelAbrahams commented Sep 19, 2020

Duplicate of #5802. Glad to see it's been fixed — it's only been five years.

@kajmagnus
Copy link

@kajmagnus kajmagnus commented Sep 26, 2020

Would it be a weird idea to optionally log warnings, and optionally even breaking the build, if a @see link is broken?

E.g. the linked-to thing got renamed / moved / removed, but the linking text wasn't updated.

@sandersn wrote:

Should invalid @link tags ever have an error?

Personally I'd want that yes — optionally breaking the build (a compiler flag?). So annoying if trying to understand old code, and it says "blah blah, see: ..." and then that other related code is ... nowhere. (At the same time, if urgently releasing a security patch, then it'd be stressful to have to spend time cleaning up documentation links? So, optionally?)

@qJake
Copy link

@qJake qJake commented Oct 1, 2020

Is there currently an option in TSC that behaves similar to Visual Studio's "Treat warnings as errors" option? If so, we could just log them as warnings and use that pre-existing flag to change them to errors if the project called for it?

noelmace added a commit to fullwebdev/fullwebdev that referenced this issue Apr 23, 2021
noelmace added a commit to fullwebdev/fullwebdev that referenced this issue Apr 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.