Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 20, 2025

The check-line-alignment rule was flattening nested markdown lists in JSDoc comments by removing indentation from continuation lines.

Changes

Detection Logic

  • Added startsWithListMarker() to identify markdown list markers (-, *, 1., 2., etc.)
  • Distinguishes list markers from JSDoc syntax hyphens (e.g., @param {type} name - description)

Indentation Preservation - "Never" Mode

  • Added shouldAllowExtraIndent() to determine when extra indentation beyond wrapIndent is valid
  • Modified wrap indent validation to allow additional indentation when:
    • A list marker exists in the current tag
    • The line has more indentation than wrapIndent
    • The indentation starts with wrapIndent (correct base)

Indentation Preservation - "Always" Mode

  • Added checkForListMarkers() to detect list markers across tag lines
  • Added calculateListExtraIndent() to calculate relative indentation for nested items
  • Modified alignment logic to preserve list structure while maintaining tag alignment:
    • Uses the first continuation line as baseline
    • Calculates each line's indentation relative to that baseline
    • Preserves relative offsets for nested list items
    • Adds wrapIndent + relative offset to the aligned position

Example

Before (broken):

/**
 * @return {Promise} Details:
 *   - On success, resolves.
 *   - On error, rejects:
 *   - When aborted, status is "abort".    // ❌ nested list flattened
 *   - On timeout, status is "timeout".     // ❌ nested list flattened
 */

After (preserved):

/**
 * @return {Promise} Details:
 *   - On success, resolves.
 *   - On error, rejects:
 *     - When aborted, status is "abort".   // ✓ nested list preserved
 *     - On timeout, status is "timeout".    // ✓ nested list preserved
 */

Scope

Original prompt

This section details on the original issue you should resolve

<issue_title>check-line-alignment screws lists in tags</issue_title>
<issue_description>## Motivation

Lists may be used in @param, @property, or @returns (random example in MediaWiki). Solutions like Rewrap, used by hundreds of thousands, automatically prettify those, making them look readable. They also support nested lists which should have correct indentation—otherwise their structure would be lost.

The check-line-alignment rule, on the other hand, is heavy-handed with lists: it either removes all indentation from the lines following the first line of the tag, or allows only fixed indentation with the wrapIndent parameter.

I suggest to implement any kind of solution that would prevent the plugin from screwing lists in tags:

  • Specifically ignoring indentation of lines following list markup like - , * , 1. if the indentation is greater (not less) than the baseline
  • Adding an option where you pass a regexp or function that would except lines from triggering the rule
  • Fully supporting markdown lists (this may be asking for too much, but this would be a perfect solution where the plugin would do the work of Rewrap itself)

Desired behavior

(From https://github.com/wikimedia/mediawiki/blob/26e679cfb2878f485ac29b8c523274a91ec0f702/resources/src/mediawiki.api/index.js#L211)

/**
 * @return {mw.Api~AbortablePromise} A promise that settles when the API response is processed.
 *   Has an 'abort' method which can be used to abort the request.
 *   See {@link mw.Api~AbortablePromise} for an example.
 *
 *   - On success, resolves to `( result, jqXHR )` where `result` is the parsed API response.
 *   - On an API error, rejects with `( code, result, result, jqXHR )` where `code` is the
 *     [API error code](https://www.mediawiki.org/wiki/API:Errors_and_warnings), and `result`
 *     is as above. When there are multiple errors, the code from the first one will be used.
 *     If there is no error code, "unknown" is used.
 *   - On other types of errors, rejects with `( 'http', details )` where `details` is an object
 *     with three fields: `xhr` (the jqXHR object), `textStatus`, and `exception`.
 *     The meaning of the last two fields is as follows:
 *     - When the request is aborted (the abort method of the promise is called), textStatus
 *       and exception are both set to "abort".
 *     - On a network timeout, textStatus and exception are both set to "timeout".
 *     - On a network error, textStatus is "error" and exception is the empty string.
 *     - When the HTTP response code is anything other than 2xx or 304 (the API does not
 *       use such response codes but some intermediate layer might), textStatus is "error"
 *       and exception is the HTTP status text (the text following the status code in the
 *       first line of the server response). For HTTP/2, `exception` is always an empty string.
 *     - When the response is not valid JSON but the previous error conditions aren't met,
 *       textStatus is "parsererror" and exception is the exception object thrown by
 *       {@link JSON.parse}.
 */

Current behavior

Note that sublists are killed.

/**
 * @return {mw.Api~AbortablePromise} A promise that settles when the API response is processed.
 *   Has an 'abort' method which can be used to abort the request.
 *   See {@link mw.Api~AbortablePromise} for an example.
 *
 *   - On success, resolves to `( result, jqXHR )` where `result` is the parsed API response.
 *   - On an API error, rejects with `( code, result, result, jqXHR )` where `code` is the
 *   [API error code](https://www.mediawiki.org/wiki/API:Errors_and_warnings), and `result`
 *   is as above. When there are multiple errors, the code from the first one will be used.
 *   If there is no error code, "unknown" is used.
 *   - On other types of errors, rejects with `( 'http', details )` where `details` is an object
 *   with three fields: `xhr` (the jqXHR object), `textStatus`, and `exception`.
 *   The meaning of the last two fields is as follows:
 *   - When the request is aborted (the abort method of the promise is called), textStatus
 *   and exception are both set to "abort".
 *   - On a network timeout, textStatus and exception are both set to "timeout".
 *   - On a network error, textStatus is "error" and exception is the empty string.
 *   - When the HTTP response code is anything other than 2xx or 304 (the API does not
 *   use such response codes but some intermediate layer might), textStatus is "error"
 *   and exception is the HTTP status text (the text following the status code in the
 *   first line of the server response). For HTTP/2, `exception` is always an empty string.
 *   - When the response ...

</details>

- Fixes gajus/eslint-plugin-jsdoc#1541

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

Copilot AI changed the title [WIP] Fix line alignment handling for lists in tags fix(check-line-alignment): preserve markdown list indentation in never mode Nov 20, 2025
Copilot AI requested a review from brettz9 November 20, 2025 21:02
Copilot finished work on behalf of brettz9 November 20, 2025 21:02
@brettz9
Copy link
Collaborator

brettz9 commented Nov 21, 2025

@copilot : Continue to implement "always" mode.

Copy link
Contributor Author

Copilot AI commented Nov 21, 2025

@copilot : Continue to implement "always" mode.

Implemented "always" mode support in commit 9d38881.

The solution preserves relative list indentation while maintaining proper tag alignment:

  • Detects list markers in tag descriptions
  • Calculates indentation relative to the first continuation line
  • Preserves nesting structure (e.g., nested items maintain their extra indentation)

Added 3 test cases covering various "always" mode scenarios with nested lists.

Copilot AI changed the title fix(check-line-alignment): preserve markdown list indentation in never mode fix(check-line-alignment): preserve markdown list indentation in never and always modes Nov 21, 2025
Copilot finished work on behalf of brettz9 November 21, 2025 14:18
Copy link
Collaborator

@brettz9 brettz9 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@brettz9 brettz9 marked this pull request as ready for review November 21, 2025 14:26
@brettz9
Copy link
Collaborator

brettz9 commented Nov 21, 2025

@copilot Anything more to add for this PR?

Copy link
Contributor Author

Copilot AI commented Nov 21, 2025

@copilot Anything more to add for this PR?

No, the PR is complete and ready to merge. It includes:

  • Full implementation for both "never" and "always" modes
  • 7 comprehensive test cases covering various list scenarios
  • 100% code coverage (3064 tests passing)
  • All documentation updated
  • Linting passes with no new issues

The solution successfully preserves nested markdown list indentation while maintaining proper tag alignment in both modes.

Copilot AI requested a review from brettz9 November 21, 2025 14:30
Copilot finished work on behalf of brettz9 November 21, 2025 14:30
@brettz9 brettz9 merged commit af2d940 into main Nov 21, 2025
16 checks passed
@brettz9 brettz9 deleted the copilot/fix-line-alignment-for-lists branch November 21, 2025 14:43
@github-actions
Copy link

🎉 This PR is included in version 61.4.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-line-alignment screws lists in tags

2 participants