diff --git a/.README/rules/check-line-alignment.md b/.README/rules/check-line-alignment.md index 6757f2a36..dd87be7bc 100644 --- a/.README/rules/check-line-alignment.md +++ b/.README/rules/check-line-alignment.md @@ -9,17 +9,16 @@ for example. This rule allows one optional string argument. If it is `"always"` then a problem is raised when the lines are not aligned. If it is `"never"` then a problem should be raised when there is more than one space between each -line's parts. Defaults to `"never"`. +line's parts. If it is `"any"`, no alignment is made. Defaults to `"never"`. -Note that in addition to alignment, both options will ensure at least one -space is present after the asterisk delimiter. +Note that in addition to alignment, the "never" and "always" options will both +ensure that at least one space is present after the asterisk delimiter. After the string, an options object is allowed with the following properties. ##### `tags` -Use this to change the tags which are sought for alignment changes. *Currently* -*only works with the "never" option.* Defaults to an array of +Use this to change the tags which are sought for alignment changes. Defaults to an array of `['param', 'arg', 'argument', 'property', 'prop', 'returns', 'return']`. ##### `customSpacings` @@ -34,10 +33,20 @@ An object with any of the following keys set to an integer. Affects spacing: If a spacing is not defined, it defaults to one. +##### `preserveMainDescriptionPostDelimiter` + +A boolean to determine whether to preserve the post-delimiter spacing of the +main description. If `false` or unset, will be set to a single space. + +##### `wrapIndent` + +The indent that will be applied for tag text after the first line. +Default to the empty string (no indent). + ||| |---|---| |Context|everywhere| -|Options|(a string matching `"always" or "never"` and optional object with `tags` and `customSpacings`)| +|Options|(a string matching `"always"`, `"never"`, or `"any"` and optional object with `tags`, `customSpacings`, `preserveMainDescriptionPostDelimiter`, and `wrapIndent`)| |Tags|`param`, `property`, `returns` and others added by `tags`| |Aliases|`arg`, `argument`, `prop`, `return`| |Recommended|false| diff --git a/README.md b/README.md index a7a054495..570abd8bd 100644 --- a/README.md +++ b/README.md @@ -2031,10 +2031,10 @@ for example. This rule allows one optional string argument. If it is `"always"` then a problem is raised when the lines are not aligned. If it is `"never"` then a problem should be raised when there is more than one space between each -line's parts. Defaults to `"never"`. +line's parts. If it is `"any"`, no alignment is made. Defaults to `"never"`. -Note that in addition to alignment, both options will ensure at least one -space is present after the asterisk delimiter. +Note that in addition to alignment, the "never" and "always" options will both +ensure that at least one space is present after the asterisk delimiter. After the string, an options object is allowed with the following properties. @@ -2042,8 +2042,7 @@ After the string, an options object is allowed with the following properties. ##### tags -Use this to change the tags which are sought for alignment changes. *Currently* -*only works with the "never" option.* Defaults to an array of +Use this to change the tags which are sought for alignment changes. Defaults to an array of `['param', 'arg', 'argument', 'property', 'prop', 'returns', 'return']`. @@ -2060,10 +2059,24 @@ An object with any of the following keys set to an integer. Affects spacing: If a spacing is not defined, it defaults to one. + + +##### preserveMainDescriptionPostDelimiter + +A boolean to determine whether to preserve the post-delimiter spacing of the +main description. If `false` or unset, will be set to a single space. + + + +##### wrapIndent + +The indent that will be applied for tag text after the first line. +Default to the empty string (no indent). + ||| |---|---| |Context|everywhere| -|Options|(a string matching `"always" or "never"` and optional object with `tags` and `customSpacings`)| +|Options|(a string matching `"always"`, `"never"`, or `"any"` and optional object with `tags`, `customSpacings`, `preserveMainDescriptionPostDelimiter`, and `wrapIndent`)| |Tags|`param`, `property`, `returns` and others added by `tags`| |Aliases|`arg`, `argument`, `prop`, `return`| |Recommended|false| @@ -2508,6 +2521,53 @@ const fn = ( lorem, sit ) => {} const fn = ( lorem, sit ) => {} // "jsdoc/check-line-alignment": ["error"|"warn", "never",{"customSpacings":{"postHyphen":2}}] // Message: Expected JSDoc block lines to not be aligned. + +/** + * @param {string} lorem Description + * with multiple lines. + */ +function quux () { +} +// "jsdoc/check-line-alignment": ["error"|"warn", "any",{"wrapIndent":" "}] +// Message: Expected wrap indent + +/** + * Function description. + * + * @param {string} lorem Description. + * @param {int} sit Description multi + * line with asterisks. + */ +const fn = ( lorem, sit ) => {} +// "jsdoc/check-line-alignment": ["error"|"warn", "always",{"wrapIndent":" "}] +// Message: Expected JSDoc block lines to be aligned. + +/** + * My function. + * + * @param {string} lorem Description. + * @param {int} sit Description multiple + * lines. + */ +const fn = ( lorem, sit ) => {} +// "jsdoc/check-line-alignment": ["error"|"warn", "never",{"wrapIndent":" "}] +// Message: Expected JSDoc block lines to not be aligned. + +/** + * @property {boolean} tls_verify_client_certificate - Whether our API should + * enable TLS client authentication + */ +function quux () {} +// "jsdoc/check-line-alignment": ["error"|"warn", "never",{"wrapIndent":" "}] +// Message: Expected wrap indent + +/** + * @property {boolean} tls_verify_client_certificate - Whether our API should + * enable TLS client authentication + */ +function quux () {} +// "jsdoc/check-line-alignment": ["error"|"warn", "never",{"wrapIndent":""}] +// Message: Expected wrap indent ```` The following patterns are not considered problems: @@ -2865,11 +2925,59 @@ const fn = ( lorem, sit ) => {}; const fn = ( a, b ) => {}; // "jsdoc/check-line-alignment": ["error"|"warn", "always"] +/** + * @param {string} lorem Description + * with multiple lines. + */ +function quux () { +} +// "jsdoc/check-line-alignment": ["error"|"warn", "any",{"wrapIndent":" "}] + +/** + * @param {string} lorem Description + * with multiple lines. + */ +function quux () { +} +// "jsdoc/check-line-alignment": ["error"|"warn", "never",{"wrapIndent":" "}] + +/** + * Function description. + * + * @param {string} lorem Description. + * @param {int} sit Description multi + * line with asterisks. + */ +const fn = ( lorem, sit ) => {} +// "jsdoc/check-line-alignment": ["error"|"warn", "always",{"wrapIndent":" "}] + +/** + * Function description. + * + * @param {string} lorem Description. + * @param {int} sit Description multi + * line with + * asterisks. + */ +const fn = ( lorem, sit ) => {} +// "jsdoc/check-line-alignment": ["error"|"warn", "always",{"wrapIndent":" "}] + +/** + * @param { + * string | number + * } lorem Description + * with multiple lines. + */ +function quux () { +} +// "jsdoc/check-line-alignment": ["error"|"warn", "never",{"wrapIndent":" "}] + /** * @param {string|string[]|TemplateResult|TemplateResult[]} event.detail.description - * Notification description */ function quux () {} +// "jsdoc/check-line-alignment": ["error"|"warn", "never",{"wrapIndent":" "}] ```` diff --git a/src/alignTransform.js b/src/alignTransform.js index dbf83e26e..a634dfc6d 100644 --- a/src/alignTransform.js +++ b/src/alignTransform.js @@ -104,6 +104,7 @@ const alignTransform = ({ tags, indent, preserveMainDescriptionPostDelimiter, + wrapIndent, }) => { let intoTags = false; let width; @@ -180,10 +181,11 @@ const alignTransform = ({ return tokens; }; - const update = (line, index, source, typelessInfo) => { + const update = (line, index, source, typelessInfo, indentTag) => { const tokens = { ...line.tokens, }; + if (tokens.tag !== '') { intoTags = true; } @@ -204,21 +206,19 @@ const alignTransform = ({ }; } - /* eslint-disable indent */ switch (tokens.delimiter) { - case Markers.start: - tokens.start = indent; - break; - case Markers.delim: - tokens.start = indent + ' '; - break; - default: - tokens.delimiter = ''; - - // compensate delimiter - tokens.start = indent + ' '; + case Markers.start: + tokens.start = indent; + break; + case Markers.delim: + tokens.start = indent + ' '; + break; + default: + tokens.delimiter = ''; + + // compensate delimiter + tokens.start = indent + ' '; } - /* eslint-enable */ if (!intoTags) { if (tokens.description === '') { @@ -240,16 +240,16 @@ const alignTransform = ({ ); // Not align. - if (!shouldAlign(tags, index, source)) { - return { - ...line, - tokens, - }; + if (shouldAlign(tags, index, source)) { + alignTokens(tokens, typelessInfo); + if (indentTag) { + tokens.postDelimiter += wrapIndent; + } } return { ...line, - tokens: alignTokens(tokens, typelessInfo), + tokens, }; }; @@ -263,10 +263,19 @@ const alignTransform = ({ const typelessInfo = getTypelessInfo(fields); + let tagIndentMode = false; + return rewireSource({ ...fields, source: source.map((line, index) => { - return update(line, index, source, typelessInfo); + const indentTag = tagIndentMode && !line.tokens.tag && line.tokens.description; + const ret = update(line, index, source, typelessInfo, indentTag); + + if (line.tokens.tag) { + tagIndentMode = true; + } + + return ret; }), }); }; diff --git a/src/rules/checkLineAlignment.js b/src/rules/checkLineAlignment.js index 12435bc54..c4e144b4e 100644 --- a/src/rules/checkLineAlignment.js +++ b/src/rules/checkLineAlignment.js @@ -137,6 +137,7 @@ const checkAlignment = ({ report, tags, utils, + wrapIndent, }) => { const transform = commentFlow( alignTransform({ @@ -144,6 +145,7 @@ const checkAlignment = ({ indent, preserveMainDescriptionPostDelimiter, tags, + wrapIndent, }), ); const transformedJsdoc = transform(jsdoc); @@ -176,6 +178,7 @@ export default iterateJsdoc(({ ], preserveMainDescriptionPostDelimiter, customSpacings, + wrapIndent = '', } = context.options[1] || {}; if (context.options[0] === 'always') { @@ -193,14 +196,48 @@ export default iterateJsdoc(({ report, tags: applicableTags, utils, + wrapIndent, }); return; } const foundTags = utils.getPresentTags(applicableTags); + if (context.options[0] !== 'any') { + for (const tag of foundTags) { + checkNotAlignedPerTag(utils, tag, customSpacings); + } + } + for (const tag of foundTags) { - checkNotAlignedPerTag(utils, tag, customSpacings); + if (tag.source.length > 1) { + let idx = 0; + for (const { + tokens, + // Avoid the tag line + } of tag.source.slice(1)) { + idx++; + + if ( + !tokens.description || + // Avoid first lines after multiline type + tokens.type || + tokens.name + ) { + continue; + } + + // Don't include a single separating space/tab + if (tokens.postDelimiter.slice(1) !== wrapIndent) { + utils.reportJSDoc('Expected wrap indent', { + line: tag.source[0].number + idx, + }, () => { + tokens.postDelimiter = tokens.postDelimiter.charAt() + wrapIndent; + }); + return; + } + } + } } }, { iterateAllJsdocs: true, @@ -213,7 +250,7 @@ export default iterateJsdoc(({ schema: [ { enum: [ - 'always', 'never', + 'always', 'never', 'any', ], type: 'string', }, @@ -250,6 +287,9 @@ export default iterateJsdoc(({ }, type: 'array', }, + wrapIndent: { + type: 'string', + }, }, type: 'object', }, diff --git a/test/rules/assertions/checkLineAlignment.js b/test/rules/assertions/checkLineAlignment.js index 1e928e447..91a140f71 100644 --- a/test/rules/assertions/checkLineAlignment.js +++ b/test/rules/assertions/checkLineAlignment.js @@ -1359,6 +1359,165 @@ export default { const fn = ( lorem, sit ) => {} `, }, + { + code: ` + /** + * @param {string} lorem Description + * with multiple lines. + */ + function quux () { + } + `, + errors: [ + { + line: 4, + message: 'Expected wrap indent', + type: 'Block', + }, + ], + options: [ + 'any', + { + wrapIndent: ' ', + }, + ], + output: ` + /** + * @param {string} lorem Description + * with multiple lines. + */ + function quux () { + } + `, + }, + { + code: ` + /** + * Function description. + * + * @param {string} lorem Description. + * @param {int} sit Description multi + * line with asterisks. + */ + const fn = ( lorem, sit ) => {} + `, + errors: [ + { + line: 2, + message: 'Expected JSDoc block lines to be aligned.', + type: 'Block', + }, + ], + options: [ + 'always', + { + wrapIndent: ' ', + }, + ], + output: ` + /** + * Function description. + * + * @param {string} lorem Description. + * @param {int} sit Description multi + * line with asterisks. + */ + const fn = ( lorem, sit ) => {} + `, + }, + { + code: ` + /** + * My function. + * + * @param {string} lorem Description. + * @param {int} sit Description multiple + * lines. + */ + const fn = ( lorem, sit ) => {} + `, + errors: [ + { + line: 6, + message: 'Expected JSDoc block lines to not be aligned.', + type: 'Block', + }, + ], + options: [ + 'never', + { + wrapIndent: ' ', + }, + ], + output: ` + /** + * My function. + * + * @param {string} lorem Description. + * @param {int} sit Description multiple + * lines. + */ + const fn = ( lorem, sit ) => {} + `, + }, + { + code: ` + /** + * @property {boolean} tls_verify_client_certificate - Whether our API should + * enable TLS client authentication + */ + function quux () {} + `, + errors: [ + { + line: 4, + message: 'Expected wrap indent', + type: 'Block', + }, + ], + options: [ + 'never', + { + wrapIndent: ' ', + }, + ], + output: ` + /** + * @property {boolean} tls_verify_client_certificate - Whether our API should + * enable TLS client authentication + */ + function quux () {} + `, + }, + { + code: ` + /** + * @property {boolean} tls_verify_client_certificate - Whether our API should + * enable TLS client authentication + */ + function quux () {} + `, + errors: [ + { + line: 4, + message: 'Expected wrap indent', + type: 'Block', + }, + ], + options: [ + 'never', + { + wrapIndent: '', + }, + ], + output: ` + /** + * @property {boolean} tls_verify_client_certificate - Whether our API should + * enable TLS client authentication + */ + function quux () {} + `, + }, ], valid: [ { @@ -1909,6 +2068,93 @@ export default { 'always', ], }, + { + code: ` + /** + * @param {string} lorem Description + * with multiple lines. + */ + function quux () { + } + `, + options: [ + 'any', + { + wrapIndent: ' ', + }, + ], + }, + { + code: ` + /** + * @param {string} lorem Description + * with multiple lines. + */ + function quux () { + } + `, + options: [ + 'never', + { + wrapIndent: ' ', + }, + ], + }, + { + code: ` + /** + * Function description. + * + * @param {string} lorem Description. + * @param {int} sit Description multi + * line with asterisks. + */ + const fn = ( lorem, sit ) => {} + `, + options: [ + 'always', + { + wrapIndent: ' ', + }, + ], + }, + { + code: ` + /** + * Function description. + * + * @param {string} lorem Description. + * @param {int} sit Description multi + * line with + * asterisks. + */ + const fn = ( lorem, sit ) => {} + `, + options: [ + 'always', + { + wrapIndent: ' ', + }, + ], + }, + { + code: ` + /** + * @param { + * string | number + * } lorem Description + * with multiple lines. + */ + function quux () { + } + `, + options: [ + 'never', + { + wrapIndent: ' ', + }, + ], + }, { code: ` /** @@ -1917,6 +2163,12 @@ export default { */ function quux () {} `, + options: [ + 'never', + { + wrapIndent: ' ', + }, + ], }, ], };