Skip to content

Commit

Permalink
feat(check-line-alignment): allow tags option to apply with "alwa…
Browse files Browse the repository at this point in the history
…ys"; #703

Adds custom align transform with support for tags

Co-authored-by: Renatho De Carli Rosa <renatho@automattic.com>
Co-authored-by: Brett Zamir <brettz9@yahoo.com>
  • Loading branch information
brettz9 and renatho committed May 10, 2021
1 parent 1b802d3 commit 4421e4e
Show file tree
Hide file tree
Showing 4 changed files with 539 additions and 22 deletions.
118 changes: 112 additions & 6 deletions README.md
Expand Up @@ -1959,6 +1959,16 @@ const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]
// Message: Expected JSDoc block lines to be aligned.

/**
* With tabs.
*
* @param {string} lorem Description.
* @param {int} sit Description multi words.
*/
const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]
// Message: Expected JSDoc block lines to be aligned.

/**
* Function description.
*
Expand Down Expand Up @@ -2090,6 +2100,19 @@ const config = {
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]
// Message: Expected JSDoc block lines to be aligned.

/**
* My object.
*
* @typedef {Object} MyObject
*
* @property {{a: number, b: string, c}} lorem Description.
* @property {Object.<string, Class>} sit Description multi words.
* @property {Object.<string, Class>} amet Description} weird {multi} {{words}}.
* @property {Object.<string, Class>} dolor
*/
// "jsdoc/check-line-alignment": ["error"|"warn", "always",{"tags":["typedef","property"]}]
// Message: Expected JSDoc block lines to be aligned.

/**
* My function.
*
Expand Down Expand Up @@ -2129,6 +2152,39 @@ const fn = ( lorem, sit ) => {}
const fn = ( lorem, sit ) => {}
// Message: Expected JSDoc block lines to not be aligned.

/**
* Function description.
*
* @param {string} lorem Description.
* @param {int} sit Description multi
line without *.
*/
const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]
// Message: Expected JSDoc block lines to be aligned.

/**
* My function.
*
* @param {string} lorem Description.
* @param {int} sit
*
* @return {string} Return description
* with multi line, but don't touch.
*/
const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always",{"tags":["param"]}]
// Message: Expected JSDoc block lines to be aligned.

/**
* Only return doc.
*
* @return {boolean} Return description.
*/
const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]
// Message: Expected JSDoc block lines to be aligned.

/**
* Creates OS based shortcuts for files, folders, and applications.
*
Expand Down Expand Up @@ -2205,6 +2261,15 @@ The following patterns are not considered problems:
const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]

/**
* With tabs.
*
* @param {string} lorem Description.
* @param {int} sit Description multi words.
*/
const fn = ( lorem, sit ) => {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]

/**
* Function description.
*
Expand Down Expand Up @@ -2279,8 +2344,8 @@ const fn = ( lorem, sit ) => {}

/**
* @namespace
* @property {object} defaults Description.
* @property {int} defaults.lorem Description multi words.
* @property {object} defaults Description.
* @property {int} defaults.lorem Description multi words.
*/
const config = {
defaults: {
Expand All @@ -2292,10 +2357,22 @@ const config = {
/**
* My object.
*
* @typedef {Object} MyObject
* @typedef {Object} MyObject
*
* @property {string} lorem Description.
* @property {int} sit Description multi words.
*/
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]

/**
* My object.
*
* @typedef {Object} MyObject
*
* @property {string} lorem Description.
* @property {int} sit Description multi words.
* @property {{a: number, b: string, c}} lorem Description.
* @property {Object.<string, Class>} sit Description multi words.
* @property {Object.<string, Class>} amet Description} weird {multi} {{words}}.
* @property {Object.<string, Class>} dolor
*/
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]

Expand All @@ -2309,7 +2386,19 @@ const config = {
* @property {Object.<string, Class>} amet Description} weird {multi} {{words}}.
* @property {Object.<string, Class>} dolor
*/
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]
// "jsdoc/check-line-alignment": ["error"|"warn", "always",{"tags":["typedef","property"]}]

/**
* My object.
*
* @template T
* @template W,X,Y,Z
* @template {string} K - K must be a string or string literal
* @template {{ serious(): string }} Seriousalizable - must have a serious method
*
* @param {{a: number, b: string, c}} lorem Description.
*/
// "jsdoc/check-line-alignment": ["error"|"warn", "always",{"tags":["template","param"]}]

/** @param {number} lorem */
const fn = ( lorem ) => {}
Expand All @@ -2324,6 +2413,23 @@ const fn = ( lorem ) => {}
function quux () {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]

/**
* Creates OS based shortcuts for files, folders, and applications.
*
* @param {object} options Options object for each OS.
* @return {boolean}
*/
function quux () {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]

/**
* Only return doc.
*
* @return {boolean} Return description.
*/
function quux () {}
// "jsdoc/check-line-alignment": ["error"|"warn", "always"]

/**
* Not validating without option.
*
Expand Down
180 changes: 180 additions & 0 deletions src/alignTransform.js
@@ -0,0 +1,180 @@
import {
Markers,
} from 'comment-parser/lib/primitives';
import {
rewireSource,
} from 'comment-parser/lib/util';

const zeroWidth = {
name: 0,
start: 0,
tag: 0,
type: 0,
};

const shouldAlign = (tags, index, source) => {
const tag = source[index].tokens.tag.replace('@', '');
const includesTag = tags.includes(tag);

if (includesTag) {
return true;
}

if (tag !== '') {
return false;
}

for (let iterator = index; iterator >= 0; iterator--) {
const previousTag = source[iterator].tokens.tag.replace('@', '');

if (previousTag !== '') {
if (tags.includes(previousTag)) {
return true;
}

return false;
}
}

return true;
};

const getWidth = (tags) => {
return (width, {tokens}, index, source) => {
if (!shouldAlign(tags, index, source)) {
return width;
}

return {
name: Math.max(width.name, tokens.name.length),
start: tokens.delimiter === Markers.start ? tokens.start.length : width.start,
tag: Math.max(width.tag, tokens.tag.length),
type: Math.max(width.type, tokens.type.length),
};
};
};

const space = (len) => {
return ''.padStart(len, ' ');
};

const alignTransform = (tags, indent) => {
let intoTags = false;
let width;

const alignTokens = (tokens) => {
const nothingAfter = {
delim: false,
name: false,
tag: false,
type: false,
};

if (tokens.description === '') {
nothingAfter.name = true;
tokens.postName = '';

if (tokens.name === '') {
nothingAfter.type = true;
tokens.postType = '';

if (tokens.type === '') {
nothingAfter.tag = true;
tokens.postTag = '';

/* istanbul ignore next: Never happens because the !intoTags return. But it's here for consistency with the original align transform */
if (tokens.tag === '') {
nothingAfter.delim = true;
}
}
}
}

tokens.postDelimiter = nothingAfter.delim ? '' : ' ';

if (!nothingAfter.tag) {
tokens.postTag = space(width.tag - tokens.tag.length + 1);
}
if (!nothingAfter.type) {
tokens.postType = space(width.type - tokens.type.length + 1);
}
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 + 1);
}

return tokens;
};

const update = (line, index, source) => {
const tokens = {...line.tokens};
if (tokens.tag !== '') {
intoTags = true;
}

const isEmpty =
tokens.tag === '' &&
tokens.name === '' &&
tokens.type === '' &&
tokens.description === '';

// dangling '*/'
if (tokens.end === Markers.end && isEmpty) {
tokens.start = indent + ' ';

return {
...line,
tokens,
};
}

/* 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 + ' ';
}
/* eslint-enable */

if (!intoTags) {
tokens.postDelimiter = tokens.description === '' ? '' : ' ';

return {
...line,
tokens,
};
}

// Not align.
if (!shouldAlign(tags, index, source)) {
return {
...line,
tokens,
};
}

return {
...line,
tokens: alignTokens(tokens),
};
};

return ({source, ...fields}) => {
width = source.reduce(getWidth(tags), {...zeroWidth});

return rewireSource({
...fields,
source: source.map(update),
});
};
};

export default alignTransform;
7 changes: 4 additions & 3 deletions src/rules/checkLineAlignment.js
@@ -1,12 +1,11 @@
import {
transforms,
} from 'comment-parser';
import alignTransform from '../alignTransform';
import iterateJsdoc from '../iterateJsdoc';

const {
flow: commentFlow,
align: commentAlign,
indent: commentIndent,
} = transforms;

const checkNotAlignedPerTag = (utils, tag) => {
Expand Down Expand Up @@ -102,9 +101,10 @@ const checkAlignment = ({
jsdoc,
jsdocNode,
report,
tags,
utils,
}) => {
const transform = commentFlow(commentAlign(), commentIndent(indent.length));
const transform = commentFlow(alignTransform(tags, indent));
const transformedJsdoc = transform(jsdoc);

const comment = '/*' + jsdocNode.value + '*/';
Expand Down Expand Up @@ -144,6 +144,7 @@ export default iterateJsdoc(({
jsdoc,
jsdocNode,
report,
tags: applicableTags,
utils,
});

Expand Down

0 comments on commit 4421e4e

Please sign in to comment.