microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.26k stars 12.38k forks source link

JSDoc unexpected leading whitespace behaviour with `@example` and `<caption>` #43955

Open davwheat opened 3 years ago

davwheat commented 3 years ago

Bug Report

🔎 Search Terms

15749 matches, but was last updated in June 2020 -- was informed to make a new issue

🕗 Version & Regression Information

⏯ Playground Link

Playground link with relevant code

💻 Code

// In the code block below, `sum(answer, 5)` is NOT indented, but it should be.
// `sum2(answer, 5)` is indented.

/**
 * Adds two numbers
 * 
 * @param a number one
 * @param b number two
 * @returns sum of number one and number two
 * 
 * @example <caption>Add 1 and 3. If answer is >0, sums result and 5.</caption>
 * ```
 * const answer = sum(1, 3); // returns 4
 * 
 * if (answer > 0) {
 *   sum(answer, 5)
 * }
 * ```
 */
function sum(a: number, b: number): number {
  return a + b
}

/**
 * Adds two numbers
 * 
 * @param a number one
 * @param b number two
 * @returns sum of number one and number two
 * 
 * @example <caption>Add 1 and 3. If answer is >0, sums result and 5.</caption>
 * ```
 *          const answer = sum2(1, 3); // returns 4
 * 
 *          if (answer > 0) {
 *            sum2(answer, 5)
 *          }
 * ```
 */
function sum2(a: number, b: number): number {
  return a + b
}

🙁 Actual behavior

The JSDoc produced for sum() has the example without any indentation, despite there being preceding whitespace.

This only happens when there is a <caption> tag present.

image

🙂 Expected behavior

The result should have the same indentation as present in the comment, preferably determined from the indentation of the code block backticks.

image

bennypowers commented 3 years ago

I came across this working on a custom elements library. I'd like to be able to document examples for custom element classes, but without indentation it's not as helpful as it could be.

https://astexplorer.net/#/gist/5f17f411fcd556f8292a46b66ec952be/latest

for example, after I convert the typescript source to a custom-elements.json, then load that manifest into a page, I get this:

Screen Shot displays an HTML snippet without any indentation

Where the source (abbreviated) is

/**
 * @element polymer-apollo-query
 *
 * `<polymer-apollo-query>` fires Polymer-style prop-changed events
 * when its `data`, `error`, `loading` or `networkStatus`
 * properties change.
 *
 * See [ApolloQueryInterface](/api/core/interfaces/query/) for more information.
 *
 * @example Querying for Data
 * ```html
 * <polymer-apollo-query
 *     data="{{data}}"
 *     variables="[[variables]]"
 *     query="[[UserQuery]]"
 * ></polymer-apollo-query>
 *
 * <paper-icon-item>
 *   <iron-image slot="item-icon">[[data.user.picture]]</iron-image>
 *   [[data.user.name]]
 * </paper-icon-item>
 * ```
 *
 * @fires data-changed
 * @fires error-changed
 * @fires errors-changed
 * @fires loading-changed
 * @fires network-status-changed
 */
export class PolymerApolloQuery<D, V> extends Super {/*...*/}
davwheat commented 2 years ago

Eek, this is still an issue. My current workaround for this is:

/**
 * @example
 * <caption>Adding a new child to a discussion list item title</caption>
 *
 * ```
 * extend(DiscussionListItem.prototype, 'view', function (vnode) {
 *   findFirstVdomChild(vnode, '.DiscussionListItem-title', (vnode) => {
 *     // ...
 *   });
 * });
 * ```
 */

Which does render correctly inside the doc pop-ups in VS Code.

image

eqprog commented 1 year ago

The above workaround will only work if you do not have a language specified.

This works, sort of, and it will let you add indentations. But its not preferred as now the entire codeblock starts after one indentation.

 * @description Using blockquote to fix indentation of code examples
 *  @example Correct Usage:
 * > ```ts 
 * > type SourceType = { required: string; optional1: string; optional2: string; omit: string };
 * > // Require and Omit keys from a SourceType:
 * > const requiredAndOptional: PickRequiredOmit<SourceType, 'required', 'omit'> = {
 * >   required: 'This value is required.',
 * >   optional1: 'This value is optional.',
 * >   // omit: 'This value cannot be provided.'
 * > };
 * > 
 * > // Omit specified keys from SourceType while making all others optional. (<R> = undefined).
 * > const omitAndOptional: PickRequiredOmit<SourceType, undefined, 'omit'> = {
 * >   optional1: 'This value is optional.',
 * >   optional2: 'This value is also optional',
 * >   // required: 'This value is technically optional, too.'
 * >   // omit: 'This value cannot be provided.'
 * > }
 * ```
 */

The final closing back ticks need to be on its own line for it to work. image

DanKaplanSES commented 10 months ago

Let me know if this is off-topic or should go in another issue, but I experience this without @example:

export class Heap<T> {
  arr: T[];
  compareFn: (first: T, second: T) => number;

  /**
   * @constructor
   * @param arr An array of type T
   * @param compareFn A comparator function. Examples:
   *
   * ```ts
   * // Min Comparator
   * [5, 4, 3, 2, 1].sort((a, b) => {
   *     return a - b;
   * }) === [1, 2, 3, 4, 5]
   *
   * // Max Comparator
   * [1, 2, 3, 4, 5].sort((a, b) => {
   *     return b - a;
   * }) === [5, 4, 3, 2, 1]
   * ```
   */
  constructor(arr: T[], compareFn: (first: T, second: T) => number) {
    this.arr = arr.sort(compareFn);
    this.compareFn = compareFn;
  }
// ...

When I hover over the constructor keyword: image

trusktr commented 8 months ago

related: