diff --git a/README.md b/README.md index 8a1f07bdf..9969e33fe 100644 --- a/README.md +++ b/README.md @@ -2697,6 +2697,79 @@ const fn = ( lorem, sit ) => {}; */ const fn2 = () => {} // "jsdoc/check-line-alignment": ["error"|"warn", "always"] + +/** + * Function description. + * + * @param lorem Description. + * @param sit Description multi words. + * @return Return description. + */ +const fn = ( lorem, sit ) => {}; + +/** + * Function description. + * + * @param lorem Description. + * @param sit Description multi words. + * @returns Return description. + */ +const fn2 = ( lorem, sit ) => {}; + +/** + * Function description. + * + * @param a Description. + * @param b Description multi words. + * @returns Return description. + */ +const fn3 = ( a, b ) => {}; +// "jsdoc/check-line-alignment": ["error"|"warn", "always"] + +/** + * Function description. + * + * @argument lorem Description. + * @return Return description. + */ +const fn = ( lorem ) => {}; + +/** + * Function description. + * + * @argument lorem Description. + * @returns Return description. + */ +const fn2 = ( lorem ) => {}; +// "jsdoc/check-line-alignment": ["error"|"warn", "always"] + +/** + * Function description. + * + * @arg a Description. + * @returns Return description. + */ +const fn = ( a ) => {}; +// "jsdoc/check-line-alignment": ["error"|"warn", "always"] + +/** + * Function description. + * + * @arg lorem Description. + * @param sit Return description. + */ +const fn = ( lorem, sit ) => {}; +// "jsdoc/check-line-alignment": ["error"|"warn", "always"] + +/** + * Function description. + * + * @arg a Description. + * @argument b Second description. + * @returns Return description. + */ +const fn = ( a, b ) => {}; +// "jsdoc/check-line-alignment": ["error"|"warn", "always"] ```` diff --git a/src/alignTransform.js b/src/alignTransform.js index ef76c7946..7a4c56d65 100644 --- a/src/alignTransform.js +++ b/src/alignTransform.js @@ -66,6 +66,35 @@ const getWidth = (tags) => { }; }; +const getTypelessInfo = (fields) => { + const hasNoTypes = fields.tags.every(({ + type, + }) => { + return !type; + }); + const maxNamedTagLength = Math.max(...fields.tags.map(({ + tag, + name, + }) => { + return name.length === 0 ? -1 : tag.length; + }).filter((length) => { + return length !== -1; + })) + 1; + const maxUnnamedTagLength = Math.max(...fields.tags.map(({ + tag, + name, + }) => { + return name.length === 0 ? tag.length : -1; + }).filter((length) => { + return length !== -1; + })) + 1; + return { + hasNoTypes, + maxNamedTagLength, + maxUnnamedTagLength, + }; +}; + const space = (len) => { return ''.padStart(len, ' '); }; @@ -79,7 +108,7 @@ const alignTransform = ({ let intoTags = false; let width; - const alignTokens = (tokens, hasNoTypes) => { + const alignTokens = (tokens, typelessInfo) => { const nothingAfter = { delim: false, name: false, @@ -107,9 +136,18 @@ const alignTransform = ({ } } - if (hasNoTypes) { + let untypedNameAdjustment = 0; + let untypedTypeAdjustment = 0; + if (typelessInfo.hasNoTypes) { nothingAfter.tag = true; tokens.postTag = ''; + if (tokens.name === '') { + untypedNameAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length; + } else { + untypedNameAdjustment = typelessInfo.maxNamedTagLength > typelessInfo.maxUnnamedTagLength ? 0 : + Math.max(0, typelessInfo.maxUnnamedTagLength - (tokens.tag.length + tokens.name.length + 1)); + untypedTypeAdjustment = typelessInfo.maxNamedTagLength - tokens.tag.length; + } } // Todo: Avoid fixing alignment of blocks with multiline wrapping of type @@ -131,18 +169,18 @@ const alignTransform = ({ } if (!nothingAfter.type) { - tokens.postType = space(width.type - tokens.type.length + spacings.postType); + tokens.postType = space(width.type - tokens.type.length + spacings.postType + untypedTypeAdjustment); } if (!nothingAfter.name) { // If post name is empty for all lines (name width 0), don't add post name spacing. - tokens.postName = width.name === 0 ? '' : space(width.name - tokens.name.length + spacings.postName); + tokens.postName = width.name === 0 ? '' : space(width.name - tokens.name.length + spacings.postName + untypedNameAdjustment); } return tokens; }; - const update = (line, index, source, hasNoTypes) => { + const update = (line, index, source, typelessInfo) => { const tokens = { ...line.tokens, }; @@ -205,7 +243,7 @@ const alignTransform = ({ return { ...line, - tokens: alignTokens(tokens, hasNoTypes), + tokens: alignTokens(tokens, typelessInfo), }; }; @@ -216,16 +254,13 @@ const alignTransform = ({ width = source.reduce(getWidth(tags), { ...zeroWidth, }); - const hasNoTypes = fields.tags.every(({ - type, - }) => { - return !type; - }); + + const typelessInfo = getTypelessInfo(fields); return rewireSource({ ...fields, source: source.map((line, index) => { - return update(line, index, source, hasNoTypes); + return update(line, index, source, typelessInfo); }), }); }; diff --git a/test/rules/assertions/checkLineAlignment.js b/test/rules/assertions/checkLineAlignment.js index e2d45d121..29236e3a5 100644 --- a/test/rules/assertions/checkLineAlignment.js +++ b/test/rules/assertions/checkLineAlignment.js @@ -1577,5 +1577,103 @@ export default { 'always', ], }, + { + code: ` + /** + * Function description. + * + * @param lorem Description. + * @param sit Description multi words. + * @return Return description. + */ + const fn = ( lorem, sit ) => {}; + + /** + * Function description. + * + * @param lorem Description. + * @param sit Description multi words. + * @returns Return description. + */ + const fn2 = ( lorem, sit ) => {}; + + /** + * Function description. + * + * @param a Description. + * @param b Description multi words. + * @returns Return description. + */ + const fn3 = ( a, b ) => {}; + `, + options: [ + 'always', + ], + }, + { + code: ` + /** + * Function description. + * + * @argument lorem Description. + * @return Return description. + */ + const fn = ( lorem ) => {}; + + /** + * Function description. + * + * @argument lorem Description. + * @returns Return description. + */ + const fn2 = ( lorem ) => {}; + `, + options: [ + 'always', + ], + }, + { + code: ` + /** + * Function description. + * + * @arg a Description. + * @returns Return description. + */ + const fn = ( a ) => {}; + `, + options: [ + 'always', + ], + }, + { + code: ` + /** + * Function description. + * + * @arg lorem Description. + * @param sit Return description. + */ + const fn = ( lorem, sit ) => {}; + `, + options: [ + 'always', + ], + }, + { + code: ` + /** + * Function description. + * + * @arg a Description. + * @argument b Second description. + * @returns Return description. + */ + const fn = ( a, b ) => {}; + `, + options: [ + 'always', + ], + }, ], };