JSDoc unexpected leading whitespace behaviour with `@example` and `<caption>`
Bug Report
🔎 Search Terms
#15749 matches, but was last updated in June 2020 -- was informed to make a new issue
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about JSDoc and leading whitespace
⏯ 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.

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

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:
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 {/*...*/}
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.

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.
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:
related:
- https://github.com/microsoft/TypeScript/issues/56583