Skip to content

Commit

Permalink
feat(tag-lines): add tags option to override behavior on a tag-sp…
Browse files Browse the repository at this point in the history
…ecific basis (including only applying to tag(s)); fixes #734
  • Loading branch information
brettz9 committed May 19, 2021
1 parent 4c6fda7 commit 6e5e76d
Show file tree
Hide file tree
Showing 4 changed files with 480 additions and 30 deletions.
17 changes: 15 additions & 2 deletions .README/rules/tag-lines.md
Expand Up @@ -4,8 +4,11 @@ Enforces lines (or no lines) between tags.

#### Options

The first option is a single string set to "always" or "never" (defaults to
"never").
The first option is a single string set to "always", "never", or "any"
(defaults to "never").

"any" is only useful with `tags` (allowing non-enforcement of lines except
for particular tags).

The second option is an object with the following optional properties.

Expand All @@ -18,6 +21,16 @@ Use with "always" to indicate the number of lines to require be present.
Use with "always" to indicate the normal lines to be added after tags should
not be added after the final tag.

##### `tags` (default to empty object)

Overrides the default behavior depending on specific tags.

An object whose keys are tag names and whose values are objects with the
following keys:

1. `lines` - Set to `always` or `never` to override.
2. `count` - Overrides main `count` (for "always")

|||
|---|---|
|Context|everywhere|
Expand Down
115 changes: 113 additions & 2 deletions README.md
Expand Up @@ -18998,8 +18998,11 @@ Enforces lines (or no lines) between tags.
<a name="eslint-plugin-jsdoc-rules-tag-lines-options-39"></a>
#### Options

The first option is a single string set to "always" or "never" (defaults to
"never").
The first option is a single string set to "always", "never", or "any"
(defaults to "never").

"any" is only useful with `tags` (allowing non-enforcement of lines except
for particular tags).

The second option is an object with the following optional properties.

Expand All @@ -19014,6 +19017,17 @@ Use with "always" to indicate the number of lines to require be present.
Use with "always" to indicate the normal lines to be added after tags should
not be added after the final tag.

<a name="eslint-plugin-jsdoc-rules-tag-lines-options-39-tags-default-to-empty-object"></a>
##### <code>tags</code> (default to empty object)

Overrides the default behavior depending on specific tags.

An object whose keys are tag names and whose values are objects with the
following keys:

1. `lines` - Set to `always` or `never` to override.
2. `count` - Overrides main `count` (for "always")

|||
|---|---|
|Context|everywhere|
Expand Down Expand Up @@ -19086,6 +19100,57 @@ The following patterns are considered problems:
*/
// "jsdoc/tag-lines": ["error"|"warn", "always"]
// Message: Expected 1 line between tags but found 0

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"tags":{"param":{"lines":"always"}}}]
// Message: Expected 1 line between tags but found 0

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"tags":{"param":{"lines":"always"}}}]
// Message: Expected 1 line between tags but found 0

/**
* Some description
* @param {string} a
* @param {number} b
*
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"tags":{"param":{"lines":"never"}}}]
// Message: Expected no lines between tags

/**
* Some description
* @param {string} a
*
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"count":2,"tags":{"param":{"lines":"always"}}}]
// Message: Expected 2 lines between tags but found 1

/**
* Some description
* @param {string} a
*
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"count":5,"tags":{"param":{"count":2,"lines":"always"}}}]
// Message: Expected 2 lines between tags but found 1

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"tags":{"anotherTag":{"lines":"never"}}}]
// Message: Expected 1 line between tags but found 0
````

The following patterns are not considered problems:
Expand Down Expand Up @@ -19138,6 +19203,52 @@ The following patterns are not considered problems:
*
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"count":2}]

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"tags":{"param":{"lines":"any"}}}]

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "always",{"tags":{"param":{"lines":"never"}}}]

/**
* Some description
* @param {number} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"tags":{"param":{"lines":"any"}}}]

/**
* Some description
* @param {number} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"tags":{"param":{"lines":"never"}}}]

/**
* Some description
* @param {string} a
*
*
* @param {number} b
*
*
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"count":5,"tags":{"param":{"count":2,"lines":"always"}}}]

/**
* Some description
* @param {string} a
* @param {number} b
*/
// "jsdoc/tag-lines": ["error"|"warn", "never",{"tags":{"anotherTag":{"lines":"always"}}}]
````


Expand Down
101 changes: 75 additions & 26 deletions src/rules/tagLines.js
Expand Up @@ -10,50 +10,80 @@ export default iterateJsdoc(({
{
count = 1,
noEndLines = false,
tags = {},
} = {},
] = context.options;

if (alwaysNever === 'never') {
jsdoc.tags.some((tg, tagIdx) => {
return tg.source.some(({tokens: {tag, name, type, description, end}}, idx) => {
const fixer = () => {
utils.removeTagItem(tagIdx, idx);
};
if (!tag && !name && !type && !description && !end) {
utils.reportJSDoc(
'Expected no lines between tags',
{line: tg.source[0].number + 1},
fixer,
true,
);

return true;
}
jsdoc.tags.some((tg, tagIdx) => {
let lastTag;

return tg.source.some(({tokens: {tag, name, type, description, end}}, idx) => {
const fixer = () => {
utils.removeTagItem(tagIdx, idx);
};
if (lastTag && tags[lastTag.slice(1)]?.lines === 'always') {
return false;
});
});
}

return;
}
if (
!tag && !name && !type && !description && !end &&
(alwaysNever === 'never' ||
lastTag && tags[lastTag.slice(1)]?.lines === 'never'
)
) {
utils.reportJSDoc(
'Expected no lines between tags',
{line: tg.source[0].number + 1},
fixer,
);

return true;
}

lastTag = tag;

return false;
});
});

(noEndLines ? jsdoc.tags.slice(0, -1) : jsdoc.tags).some((tg, tagIdx) => {
const lines = [];

let currentTag;
tg.source.forEach(({number, tokens: {tag, name, type, description, end}}, idx) => {
if (tag) {
currentTag = tag;
}
if (!tag && !name && !type && !description && !end) {
lines.push({idx, number});
}
});
if (lines.length < count) {

const currentTg = currentTag && tags[currentTag.slice(1)];
const tagCount = currentTg?.count;

const defaultAlways = alwaysNever === 'always' && currentTg?.lines !== 'never' &&
currentTg?.lines !== 'any' && lines.length < count;

let overrideAlways;
let fixCount = count;
if (!defaultAlways) {
fixCount = typeof tagCount === 'number' ? tagCount : count;
overrideAlways = currentTg?.lines === 'always' &&
lines.length < fixCount;
}

if (defaultAlways || overrideAlways) {
const fixer = () => {
utils.addLines(tagIdx, lines[lines.length - 1]?.idx || 1, count - lines.length);
utils.addLines(tagIdx, lines[lines.length - 1]?.idx || 1, fixCount - lines.length);
};
const line = lines[lines.length - 1]?.number || tg.source[0].number;
utils.reportJSDoc(
`Expected ${count} line${count === 1 ? '' : 's'} between tags but found ${lines.length}`,
{line: lines[lines.length - 1]?.number || tg.source[0].number},
`Expected ${fixCount} line${fixCount === 1 ? '' : 's'} between tags but found ${lines.length}`,
{
line,
},
fixer,
true,
);

return true;
Expand All @@ -71,7 +101,7 @@ export default iterateJsdoc(({
fixable: 'code',
schema: [
{
enum: ['always', 'never'],
enum: ['always', 'any', 'never'],
type: 'string',
},
{
Expand All @@ -83,6 +113,25 @@ export default iterateJsdoc(({
noEndLines: {
type: 'boolean',
},
tags: {
properties: {
patternProperties: {
'.*': {
additionalProperties: false,
properties: {
count: {
type: 'integer',
},
lines: {
enum: ['always', 'never'],
type: 'string',
},
},
},
},
},
type: 'object',
},
},
type: 'object',
},
Expand Down

0 comments on commit 6e5e76d

Please sign in to comment.