Skip to content

Commit c4bbdd9

Browse files
committed
feat(tag-lines): maxBlockLines option; fixes #1346
1 parent c8612b2 commit c4bbdd9

File tree

6 files changed

+401
-10
lines changed

6 files changed

+401
-10
lines changed

.README/rules/tag-lines.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Removes or adds lines between tags or trailing tags.
2727
|Tags|Any|
2828
|Recommended|true|
2929
|Settings|N/A|
30-
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `startLines`, `tags`|
30+
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `maxBlockLines`, `startLines`, `tags`|
3131

3232
## Failing examples
3333

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ non-default-recommended fixer).
488488
||| [require-yields-description](./docs/rules/require-yields-description.md#readme) | Requires a description for `@yields` tags |
489489
|:heavy_check_mark:|| [require-yields-type](./docs/rules/require-yields-type.md#readme) | Requires a type for `@yields` tags |
490490
||:wrench:| [sort-tags](./docs/rules/sort-tags.md#readme) | Sorts tags by a specified sequence according to tag name, optionally adding line breaks between tag groups. |
491-
|:heavy_check_mark:|:wrench:| [tag-lines](./docs/rules/tag-lines.md#readme) | Enforces lines (or no lines) between tags. |
491+
|:heavy_check_mark:|:wrench:| [tag-lines](./docs/rules/tag-lines.md#readme) | Enforces lines (or no lines) before, after, or between tags. |
492492
||:wrench:| [text-escaping](./docs/rules/text-escaping.md#readme) | Auto-escape certain characters that are input within block and tag descriptions. |
493493
||:wrench:| [type-formatting](./docs/rules/type-formatting.md#readme) | Formats JSDoc type values. |
494494
|:heavy_check_mark:|| [valid-types](./docs/rules/valid-types.md#readme) | Requires all types/namepaths to be valid JSDoc, Closure compiler, or TypeScript types (configurable in settings). |

docs/rules/tag-lines.md

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* [`applyToEndTag`](#user-content-tag-lines-options-applytoendtag)
88
* [`count`](#user-content-tag-lines-options-count)
99
* [`endLines`](#user-content-tag-lines-options-endlines)
10+
* [`maxBlockLines`](#user-content-tag-lines-options-maxblocklines)
1011
* [`startLines`](#user-content-tag-lines-options-startlines)
1112
* [`tags`](#user-content-tag-lines-options-tags)
1213
* [Context and settings](#user-content-tag-lines-context-and-settings)
@@ -35,7 +36,7 @@ Removes or adds lines between tags or trailing tags.
3536

3637
The first option is a string with the following possible values: "always", "any", "never".
3738
Defaults to "never". "any" is only useful with `tags` (allowing non-enforcement of lines except
38-
for particular tags) or with `startLines` or `endLines`. It is also
39+
for particular tags) or with `startLines`, `endLines`, or `maxBlockLines`. It is also
3940
necessary if using the linebreak-setting options of the `sort-tags` rule
4041
so that the two rules won't conflict in both attempting to set lines
4142
between tags.
@@ -65,6 +66,15 @@ If not set to `null`, will enforce end lines to the given count on the
6566
final tag only.
6667

6768
Defaults to `0`.
69+
<a name="user-content-tag-lines-options-maxblocklines"></a>
70+
<a name="tag-lines-options-maxblocklines"></a>
71+
### <code>maxBlockLines</code>
72+
73+
If not set to `null`, will enforce a maximum number of lines to the given count anywhere in the block description.
74+
75+
Note that if non-`null`, `maxBlockLines` must be greater than or equal to `startLines`.
76+
77+
Defaults to `null`.
6878
<a name="user-content-tag-lines-options-startlines"></a>
6979
<a name="tag-lines-options-startlines"></a>
7080
### <code>startLines</code>
@@ -99,7 +109,7 @@ Defaults to empty object.
99109
|Tags|Any|
100110
|Recommended|true|
101111
|Settings|N/A|
102-
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `startLines`, `tags`|
112+
|Options|string ("always", "any", "never") followed by object with `applyToEndTag`, `count`, `endLines`, `maxBlockLines`, `startLines`, `tags`|
103113

104114
<a name="user-content-tag-lines-failing-examples"></a>
105115
<a name="tag-lines-failing-examples"></a>
@@ -288,13 +298,23 @@ The following patterns are considered problems:
288298
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLines":1}]
289299
// Message: Expected only 1 line after block description
290300

301+
/**
302+
* Some description
303+
*
304+
*
305+
*
306+
* @param {string} a
307+
*/
308+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLines":2}]
309+
// Message: Expected only 2 lines after block description
310+
291311
/**
292312
* Some description
293313
*
294314
* @param {string} a
295315
*/
296316
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLines":0}]
297-
// Message: Expected only 0 line after block description
317+
// Message: Expected only 0 lines after block description
298318

299319
/**
300320
* Some description
@@ -310,6 +330,68 @@ The following patterns are considered problems:
310330
*/
311331
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLines":1}]
312332
// Message: Expected 1 lines after block description
333+
334+
/**
335+
*
336+
* Some description
337+
*
338+
* Abc
339+
*
340+
*
341+
*
342+
* Def
343+
*
344+
* @param {string} a
345+
*/
346+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"maxBlockLines":2}]
347+
// Message: Expected a maximum of 2 lines within block description
348+
349+
/**
350+
*
351+
* Some description
352+
*
353+
* Abc
354+
*
355+
*
356+
*
357+
* Def
358+
*
359+
* @param {string} a
360+
*/
361+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"maxBlockLines":1}]
362+
// Message: Expected a maximum of 1 line within block description
363+
364+
/**
365+
*
366+
* Some description
367+
*
368+
* Abc
369+
*
370+
*
371+
*
372+
* Def
373+
*
374+
* @param {string} a
375+
*/
376+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"maxBlockLines":0}]
377+
// Message: Expected a maximum of 0 lines within block description
378+
379+
/**
380+
*
381+
* Some description
382+
*
383+
* Abc
384+
*
385+
*
386+
* Def
387+
*
388+
*
389+
*
390+
*
391+
* @param {string} a
392+
*/
393+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"maxBlockLines":2,"startLines":5}]
394+
// Message: If set to a number, `maxBlockLines` must be greater than or equal to `startLines`.
313395
````
314396

315397

@@ -544,5 +626,17 @@ class _Foo {
544626
}
545627
}
546628
// "jsdoc/tag-lines": ["error"|"warn", "any",{"startLines":1}]
629+
630+
/**
631+
*
632+
* Some description
633+
*
634+
* Abc
635+
*
636+
*
637+
* Def
638+
* @param {string} a
639+
*/
640+
// "jsdoc/tag-lines": ["error"|"warn", "any",{"maxBlockLines":2}]
547641
````
548642

src/rules.d.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2806,7 +2806,7 @@ export interface Rules {
28062806
}
28072807
];
28082808

2809-
/** Enforces lines (or no lines) between tags. */
2809+
/** Enforces lines (or no lines) before, after, or between tags. */
28102810
"jsdoc/tag-lines":
28112811
| []
28122812
| ["always" | "any" | "never"]
@@ -2833,6 +2833,14 @@ export interface Rules {
28332833
* Defaults to `0`.
28342834
*/
28352835
endLines?: number | null;
2836+
/**
2837+
* If not set to `null`, will enforce a maximum number of lines to the given count anywhere in the block description.
2838+
*
2839+
* Note that if non-`null`, `maxBlockLines` must be greater than or equal to `startLines`.
2840+
*
2841+
* Defaults to `null`.
2842+
*/
2843+
maxBlockLines?: number | null;
28362844
/**
28372845
* If not set to `null`, will enforce end lines to the given count before the
28382846
* first tag only, unless there is only whitespace content, in which case,

src/rules/tagLines.js

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,66 @@
11
import iterateJsdoc from '../iterateJsdoc.js';
22

3+
/**
4+
* @param {{
5+
* maxBlockLines: null|number,
6+
* startLines: null|number,
7+
* utils: import('../iterateJsdoc.js').Utils
8+
* }} cfg
9+
*/
10+
const checkMaxBlockLines = ({
11+
maxBlockLines,
12+
startLines,
13+
utils,
14+
}) => {
15+
if (typeof maxBlockLines !== 'number') {
16+
return false;
17+
}
18+
19+
if (typeof startLines === 'number' && maxBlockLines < startLines) {
20+
utils.reportJSDoc(
21+
'If set to a number, `maxBlockLines` must be greater than or equal to `startLines`.',
22+
);
23+
return true;
24+
}
25+
26+
const {
27+
description,
28+
} = utils.getDescription();
29+
const excessBlockLinesRegex = new RegExp('\n{' + (maxBlockLines + 2) + ',}', 'v');
30+
const excessBlockLinesMatch = description.match(excessBlockLinesRegex);
31+
const excessBlockLines = excessBlockLinesMatch?.[0]?.length ?? 0;
32+
if (excessBlockLinesMatch) {
33+
const excessIndexLine = description.slice(0, excessBlockLinesMatch.index).match(/\n/gv)?.length ?? 0;
34+
utils.reportJSDoc(
35+
`Expected a maximum of ${maxBlockLines} line${maxBlockLines === 1 ? '' : 's'} within block description`,
36+
{
37+
line: excessIndexLine,
38+
},
39+
() => {
40+
utils.setBlockDescription((info, seedTokens, descLines) => {
41+
return [
42+
...descLines.slice(0, excessIndexLine),
43+
...descLines.slice(excessIndexLine + excessBlockLines - 1 - maxBlockLines),
44+
].map((desc) => {
45+
return {
46+
number: 0,
47+
source: '',
48+
tokens: seedTokens({
49+
...info,
50+
description: desc,
51+
postDelimiter: desc.trim() ? ' ' : '',
52+
}),
53+
};
54+
});
55+
});
56+
},
57+
);
58+
return true;
59+
}
60+
61+
return false;
62+
};
63+
364
export default iterateJsdoc(({
465
context,
566
jsdoc,
@@ -11,6 +72,7 @@ export default iterateJsdoc(({
1172
applyToEndTag = true,
1273
count = 1,
1374
endLines = 0,
75+
maxBlockLines = null,
1476
startLines = 0,
1577
tags = {},
1678
} = {},
@@ -218,6 +280,14 @@ export default iterateJsdoc(({
218280
return false;
219281
});
220282

283+
if (checkMaxBlockLines({
284+
maxBlockLines,
285+
startLines,
286+
utils,
287+
})) {
288+
return;
289+
}
290+
221291
if (typeof startLines === 'number') {
222292
if (!jsdoc.tags.length) {
223293
return;
@@ -235,7 +305,7 @@ export default iterateJsdoc(({
235305
const trailingDiff = (trailingLines ?? 0) - startLines;
236306
if (trailingDiff > 0) {
237307
utils.reportJSDoc(
238-
`Expected only ${startLines} line after block description`,
308+
`Expected only ${startLines} line${startLines === 1 ? '' : 's'} after block description`,
239309
{
240310
line: lastDescriptionLine - trailingDiff,
241311
},
@@ -290,14 +360,14 @@ export default iterateJsdoc(({
290360
iterateAllJsdocs: true,
291361
meta: {
292362
docs: {
293-
description: 'Enforces lines (or no lines) between tags.',
363+
description: 'Enforces lines (or no lines) before, after, or between tags.',
294364
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/tag-lines.md#repos-sticky-header',
295365
},
296366
fixable: 'code',
297367
schema: [
298368
{
299369
description: `Defaults to "never". "any" is only useful with \`tags\` (allowing non-enforcement of lines except
300-
for particular tags) or with \`startLines\` or \`endLines\`. It is also
370+
for particular tags) or with \`startLines\`, \`endLines\`, or \`maxBlockLines\`. It is also
301371
necessary if using the linebreak-setting options of the \`sort-tags\` rule
302372
so that the two rules won't conflict in both attempting to set lines
303373
between tags.`,
@@ -335,6 +405,21 @@ Defaults to 1.`,
335405
final tag only.
336406
337407
Defaults to \`0\`.`,
408+
},
409+
maxBlockLines: {
410+
anyOf: [
411+
{
412+
type: 'integer',
413+
},
414+
{
415+
type: 'null',
416+
},
417+
],
418+
description: `If not set to \`null\`, will enforce a maximum number of lines to the given count anywhere in the block description.
419+
420+
Note that if non-\`null\`, \`maxBlockLines\` must be greater than or equal to \`startLines\`.
421+
422+
Defaults to \`null\`.`,
338423
},
339424
startLines: {
340425
anyOf: [

0 commit comments

Comments
 (0)