TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

Provide way to link to other files from JSDoc comments

Open mjbvz opened this issue 3 years ago â€Ē 32 comments

Suggestion

🔍 Search Terms

  • quickInfo
  • markdown
  • link
  • relative path
  • jsdoc

⭐ Suggestion

On VS Code, we have a long standing feature request to support relative file paths inside JSDoc comments: https://github.com/microsoft/vscode/issues/86564

For example, if if I have a file folder/foo.ts:

/**
 * [link](./other.js)
 */
export declare function foo(x: string, y: {}): void

Then anywhere the documentation for foo is shown, we should render link as a clickable link to folder/other.ts.

On the VS Code side today, we can support rendering link but do not know what relative paths inside the documentation should be relative to

Proposal

To fix this, I propose that requests that return documentation also include a path that tells us which file the documentation comes from

Here's an example using quickInfo:

[Trace  - 17:11:37.514] <semantic> Response received: quickinfo (292). Request took 2 ms. Success: true 
Result: {
    "kind": "function",
    "kindModifiers": "export,declare",
    "start": { "line": 4, "offset": 25    },
    "end": { "line": 4, "offset": 28 },
    "displayString": "function foo(x: string, y: {}): void",
    "documentation": [
        {
            "text": "[link](./files.js)",
            "kind": "text",

             // new field
             "file": "/absolute/path/to/folder/foo.ts"
        }
    ],
    "tags": []
}

This would likely be a new field on SymbolDisplayPart

mjbvz avatar Feb 03 '22 17:02 mjbvz

Not a huge fan of my original proposal actually

Instead of markdown, I think we should investigate adding proper file link support to JSDoc. As far as I can tell this isn't supported in the jsdoc spec

Here's what this could look like using our existing @link support:

/**
 * {@link import('./main.ts') link}
 */
declare function foo(x: string, y: {}): void

Or we could come up with some new way to express file links:

/**
 * {@linkfile ./main.ts link}
 */
export declare function foo(x: string, y: {}): void

Having proper semantic file link support would be nice as we could include these references when you run find all reference to file and update them when you move a file

mjbvz avatar Feb 03 '22 17:02 mjbvz

FWIW there's not necessarily one file. In some cases we collate the documentation from multiple declarations.

RyanCavanaugh avatar Feb 03 '22 17:02 RyanCavanaugh

From what I understand, you guys are saying that this is blocking https://github.com/microsoft/vscode/issues/86564.

Are there any updates on this? Would love to be able to link files, be it JS, or any file really.

  • I would also like to ask if its possible to add line/column numbers, as though you just throw it to the code CLI?
    • E.g. {@linkfile ../../x/y/z.js:42}
  • And another one: could we have support to have file links relative to a module name, iff the url does not start with (i) ., (ii) root or (iii) protocol?
    • E.g. {@linkfile my-module/x/y/z.js:42} instead of {@linkfile ../../../node_modules/my-module/x/y/z.js:42}?

Domiii avatar May 16 '22 14:05 Domiii

We have a component that's using webhooks so it needs a public url to work and we wrote up docs on how to setup ngrok to use it and then hit the question of where to store it - I couldn't get anything to work well so adding my thoughts here for the discussion:

  • ultimately any solution probably should work with @see, @link, and [markdown-style](links) but it might be tricky to stay backward compatible

  • personally I'd love if [link](./path) was relative to the current file location like it is today but there was something else like [link](/path) relative to the repo root since I'm likely to want to reference things there and things that aren't in the usual import paths

/**
  Uses the [foo-service's bar endpoint](https://github.com/org/foo-service/blob/trunk/docs/bar.md) to do things.

  Also look at our relative [doc](./doc.md) and our top-level [README](/README.md)

  @see {@link /README.md#using-foo-service how to use the foo service}
  @see /docs/using-services.md#foo
*/
  • lastly, the only way for my foo function in foo.ts to reference bar in bar.ts is to import bar from ./bar but our code tooling will correctly say that bar isn't used and remove that import on save - so I think @see and @link would further benefit with a syntax for this that can also reference exported members so that clicking scrolls to the right line that references it - I saw things like @see BarClass#barProperty already works for things that are imported, so maybe something like @see ./bar.ts#BarClass#barProperty for things that aren't imported - double hash tag is a little odd but it's the most semantic thing I can think of cause it literally is an anchor to a spot in a file and it stays similar to the other syntax for class members
// -- foo.ts ---------------------
/**
  This function does foo stuff.
  See [bar](./bar.ts#bar) for an example of how to use it.

  @see ./bar.ts#BarClass#barProperty
*/
function foo() {}

// -- bar.ts ---------------------
import foo from './foo'

function bar() {
  foo()
}

class BarClass {
  barProperty: string
}

(Also for consideration: it'd ultimately be important for vscode and similar editors to be able to update the file paths here when files are renamed and the function, class, and property names when they're renamed to avoid breaking links, I'm assuming TS helps to feed data for those features to work.)

michaeldrotar avatar Aug 22 '22 12:08 michaeldrotar

It shouldn't just support line numbers, it should also support references to symbols (and other jsdoc created symbols).

Typedoc provides something similar to this.

We have design system components that use keys of our design system tokens as an abstraction. We'd like to be annotating those props with a link to the actual design tokens so it can help developers interpret figma designs.

If we were only able to use line numbers, then they'd become a problem to update over time.

airtonix avatar Dec 05 '22 04:12 airtonix

It shouldn't just support line numbers, it should also support references to symbols (and other jsdoc created symbols).

Agreed. That would be fantastic. Line numbers would, over time, always end up being incorrect, and cannot be verified by some sort of "link checker" either

Domiii avatar Dec 05 '22 07:12 Domiii

Rather than linking to a line number, could such a link... link to another specific symbol? @link theImportantDocumentation-> @target theImportantDocumentation ?

GlenPierce-Workday avatar Mar 15 '23 19:03 GlenPierce-Workday

+1 Please add native support.

joshuambg avatar Jun 21 '23 01:06 joshuambg

Loving +1 that a user-friendly way of simply using @link to refer to a relative file would be great. Personally would love inline for the format @see {@link ./myFile.js}

nickyonge avatar Jun 21 '23 22:06 nickyonge

+1 for @nickyonge proposal.

lflorent avatar Sep 14 '23 12:09 lflorent

I think, these are the three main wishes expressed in this thread:

  1. relative links (ref)
  2. direct links to dependencies (ref)
  3. support for line + column numbers (ref)

Domiii avatar Sep 14 '23 13:09 Domiii

+1 to {@link 'relative/path/file.ext:row?:col?'

Don't know much about the JSDoc ecosystem, but would be nice if it could resolve paths specified in jsconfig.js and tsconfig.ts. I guess this behaviour could be a flag in the IDE settings for better SOC and ultimately JSDoc configuration itself needing to support path mappings for this particular functionality.

On an unrelated note... I soLvEd iT gUyS ðŸĪŠ: {@link file://./sibling.ext} -- I mean it does actually work for me on [email protected] and [email protected], but only for paths starting with ./ coudn't make ../ work lol. Absolute paths work for anything though if you wanna go there: file:///Users/babyshark/Desktop/doodoo.ext

js1m avatar Sep 23 '23 19:09 js1m

@js1m I was able to make ../ work by doing this {@link file://./../../sibling.ext}

sagargurtu avatar Oct 17 '23 09:10 sagargurtu

thank you @sagargurtu, I think it is not a TypeScript thing but a VS Code thing, it allows you to reference file in a comment.

You can do this:

/**
 * Some bla bla from file://./../../lib/Task/index.ts#ITask
 */
const updateReport = (id: string, selected: ITask[]) => {}

And it actually works, you can navigate to that file.

huksley avatar Oct 18 '23 07:10 huksley

@huksley Are you sure that you don't have any extentions which provides this? Because for me it is not working. Neiter locally nor wsl. VS Code is always showing an error message "Unable to open.." and that it cannot create the file

grafik

terenc3 avatar Oct 18 '23 07:10 terenc3

@terenc3 Try

/**
 *
 */

comment style. I don't think I have special extensions to handle this. I use Mac OS if that makes a difference.

huksley avatar Oct 18 '23 21:10 huksley

/**
 * file://./../package.json
 */

Tried this on Mac OS and it works, on Windows 11 it doesn't: Unable to open 'package.json' 😞

madhugod avatar Dec 13 '23 13:12 madhugod

It would be great if a solution to this would support clickable links in both the document editor and the rendered hover.

I'm on Mac OS v14.3.1 VSCode 1.90.1 and found - with all extensions disabled, editor.links not explicitly set, and despite https://github.com/microsoft/vscode/issues/86564 still open (?):

/**
 * [link](./other.ts) 
 * works in rendered hover, but not in document
 */
Screen Capture

https://github.com/microsoft/TypeScript/assets/14281373/812ad386-4263-44d8-aab0-dac107860633

and (as has been mentioned, since 2020 - although not on windows?):

/**
 * file://./other.ts
 * works in document, but not in rendered hover
 */
I tried (unsuccessfully) to combine them to work in both.
 * // explicit relative paths
 * [markdown explicit](./other.ts) - rendered:works, document:not working
 * [markdown with explicit file](file://./other.ts) - rendered:wrong location, document:works
 * {@link file://./other.ts|link explicit} - rendered:not working, document:wrong location
 * {@link file://./other.ts} - rendered:not working, document:works
 * {@link [link markdown explicit](file://./other.ts)} - rendered:wrong location, document:works
 * file://./other.ts - rendered:not working, document:works
 * 
 * // implicit relative paths
 * [markdown implicit](other.ts) - rendered:works, document:not working
 * [markdown with implicit file](file://other.ts)  - rendered:wrong location, document:wrong location
 * {@link file://other.ts|link implicit} - rendered:not working, document:wrong location
 * {@link file://other.ts} - rendered:not working, document:wrong location
 * {@link [link markdown implicit](file://other.ts)} - rendered:wrong location, document:wrong location
 * file://other.ts - rendered:not working, document:wrong location
 */

Screenshot 2024-06-19 at 04 18 27 Screenshot 2024-06-19 at 04 18 18

Another way to use @link is to point it to a variable. Here I wanted to link to my Server class connect method but the current file did not import Server directly. So, I did this:

/** Schema for {@link projectService.repo.server.connect} options */

ghost avatar Jun 23 '24 17:06 ghost

The way we've been doing it at work is with type-only imports. An excerpt from our standards document, from the part on how we mark deprecations:

// file: newFunction.ts
export function newFunction() {}

//file: deprecatedFunction.ts
import type { newFunction } from "./newFunction"

/** @deprecated use {@link newFunction} */
function deprecatedFunction() {}

This results in clickable links, without impacting emitted code.

legowerewolf avatar Jun 23 '24 19:06 legowerewolf

You can already link to other files. You can do

/** @see file:///C:/jsproject/jsfile.js */
function foo() { }

And VSCode will find it. The problem is more that we don't have a way to specify relative paths with file:// so everything needs to be absolute.

You can also link to types declared in other files (if Typescript picks it up):

/** @see {FooObject} */
function foo() { }

You can even link to functions:

function foo() { }

/** @see {foo} */
function bar() { }

You cannot do this across files since import will not work. but @template does work, so you can hack the link into it with a useless template.

/** @template {import('./barfile.js').barFn} T99 */
function foo() { }

The only caveat is that the type must be exported.

You can also use @typedef instead, though it's leaky:

/**
 * @typedef {import('./barfile.js').barFn} ref
 * @see {ref}
 */
function foo() { }

clshortfuse avatar Jul 07 '24 17:07 clshortfuse

This works in VSCode:

/**
 * {@link file://./foo.ts}
 */

Special note for when you try to go a level above, you still need to start with ./:

/**
 * {@link file://./../bar.ts}
 */

romulof avatar Jul 24 '24 19:07 romulof

Thanks @romulof, for the info about parent directory traversal. I additionally would like to add that the hyperlink rendering doesn't render as expected, but the functionality works when used with a relative path

/**
 * @see {@link file://./../../../@types/foo.d.ts}
 * @see {@link http://github.com}
 * @see {@link file://./../../../@types/foo.d.ts | Foo}
 * @see {@link http://github.com | GitHub}
 */

image image

seahindeniz avatar Aug 11 '24 09:08 seahindeniz

Whilst waiting for a REAL solution... Why not combine both?

Simple but repetitive... I appreciate how irritatingly un-DRY it is though :grimacing:. I'm just happy I can click through to the file via both comment or tooltip :tada:. Figured out after reading the JSDoc docs.

Code Snippet

my .eslintrc.cjs

/**
 * See the [eslint docs]({@link http://eslint.org/docs/user-guide/configuring}) for info on how to configure eslint files.\
 * Check out [these docs]({@link https://github.com/Shinigami92/eslint-define-config}) for help with how to use `defineConfig` and `defineFlatConfig`.
 *
 * ### Other lint configs:
 * - [Unit Tests]({@link ./tests/unit/.eslintrc.cjs})
 * - [E2E Tests]({@link ./tests/e2e/.eslintrc.cjs})
 */

How comment renders in VSCode

Image

How tooltip renders in VSCode

Image

EmilyRosina avatar Sep 24 '24 14:09 EmilyRosina

same need + 1

lake2 avatar Sep 27 '24 06:09 lake2

+1 requesting

DDD-fx avatar Dec 11 '24 10:12 DDD-fx

@EmilyRosina Your suggestion worked perfectly. I've looked for a solution to this about a dozen times over the past few years- this is the only time I've found a working solution. Thank you!

xbtmatt avatar Feb 28 '25 23:02 xbtmatt

Linking to files I think has always worked, I think. But linking to objects/classes/function within another file isn't as straightforward. Maybe I should open up another issue specifically for that.

clshortfuse avatar Mar 01 '25 17:03 clshortfuse

Whilst waiting for a REAL solution... Why not combine both?

Simple but repetitive... I appreciate how irritatingly un-DRY it is though 😎. I'm just happy I can click through to the file via both comment or tooltip 🎉. Figured out after reading the JSDoc docs.

Code Snippet

my .eslintrc.cjs

/**
 * See the [eslint docs]({@link http://eslint.org/docs/user-guide/configuring}) for info on how to configure eslint files.\
 * Check out [these docs]({@link https://github.com/Shinigami92/eslint-define-config}) for help with how to use `defineConfig` and `defineFlatConfig`.
 *
 * ### Other lint configs:
 * - [Unit Tests]({@link ./tests/unit/.eslintrc.cjs})
 * - [E2E Tests]({@link ./tests/e2e/.eslintrc.cjs})
 */

How comment renders in VSCode

Image

How tooltip renders in VSCode

Image

BTW, file:// protocol can support specify line number, for example:

/**
 * - [Unit tests](file:///D:/project/tests/foo.test.js#L12)
 */

But I have to specify absolute path(relative path not work, I don't know why)

lovetingyuan avatar Jul 23 '25 01:07 lovetingyuan

The solution from @EmilyRosina gives me clickable, correctly rendered tooltip links, but I cannot seem to click the links in the editor directly. Possibly the suggestion was also to use file://./-style links? If I create both, one is clickable in the editor (and renders as ugly in the tooltip), and the style @EmilyRosina suggested is clickable in the tooltip (only).

This is the best solution I've come up with, that yields an easily clickable link in both places while minimizing the ugly:

	 * See [Loader.java]({@link ./Loader.java}). (file://./Loader.java}

(To be clear, this link is within a TypeScript file, but is linking to associated Java code.)

davidpcaldwell avatar Sep 10 '25 12:09 davidpcaldwell