@d3lm/lint-preset 1.6.1 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -258,10 +258,11 @@ Verdicts are layout issues that can be measured without reading the prose. They
258
258
  - `underfill` means a line ends early, so the paragraph needs more lines than it should
259
259
  - `widow` means fewer than three words sit on the last line of a paragraph
260
260
  - `unbalanced` means the lines are less even than in the suggested rewrap, or a line ends inside a link, code span, or `{...}` group (see [Text That Stays on One Line](#text-that-stays-on-one-line))
261
+ - `wide` means a line ends more than `tolerance` columns past `width`, and past every line of the suggested rewrap, but within `maxWidth`
261
262
 
262
263
  Hints come from heuristics and can be false positives, because only the meaning of the prose shows whether the rule applies. A hint only sends a comment to the agent, which decides whether to change it. `check` only prints comments with verdicts, so a passing check stays quiet apart from its summary, even when hints remain after review. Use `list --suspicious` to inspect hints, which are marked with `(hint)` and never fail a check:
263
264
 
264
- - `orphan` means a line ends with the first word of a sentence, a line ends with the first two words of a sentence that the suggested rewrap moves to the next line, or the first of two lines ends with the start of a sentence that fits on the second without making it longer than the first
265
+ - `orphan` means a line ends with the first word of a sentence or parenthetical, or with the first word after a semicolon or colon (but not after a comma), a line ends with the first two words of a sentence that the suggested rewrap moves to the next line, or the first of two lines ends with the start of a sentence that fits on the second without making it longer than the first
265
266
  - `split-parenthetical` means a line ends inside a parenthetical that the suggested rewrap keeps on one line within `maxWidth`
266
267
  - `em-dash` means the comment uses `—`
267
268
  - `dash` means the comment uses `–` or `--` between words
@@ -274,9 +275,9 @@ A paragraph with an `orphan` hint is not reported as `unbalanced` when that word
274
275
 
275
276
  In the same way, a paragraph that splits a parenthetical is not reported as `unbalanced` when the split is all that sets it apart from the suggested rewrap, because the split can read as well and keep the line closer to `width`. This holds when the lines before the split match the rewrap and the lines after it match the rewrap of the text after the split. Otherwise the paragraph is still `unbalanced`.
276
277
 
277
- With a `tolerance`, a paragraph has no `underfill`, `widow`, or `unbalanced` verdict when it already has the layout that the tool produces for a wider `width`, up to `width` plus `tolerance`. Its suggestion then keeps the lines as they are. With `width: 100` and `tolerance: 5`, a paragraph that is laid out the way the tool lays it out for a width of 105 passes. A paragraph that only has hints keeps them, and a line past `maxWidth` is still an `overflow`.
278
+ With a `tolerance`, a paragraph has no `underfill`, `widow`, `unbalanced`, or `wide` verdict when it already has the layout that the tool produces for a wider `width`, up to `width` plus `tolerance`. Its suggestion then keeps the lines as they are. With `width: 100` and `tolerance: 5`, a paragraph that is laid out the way the tool lays it out for a width of 105 passes. A paragraph that only has hints keeps them, and a line past `maxWidth` is still an `overflow`.
278
279
 
279
- The `tolerance` also holds for each line on its own, in both directions. A paragraph is not `unbalanced` when it has as many lines as the suggested rewrap and no line is more than `tolerance` columns shorter or longer than the same line of the rewrap. Its suggestion then keeps the lines as they are too. With `tolerance: 5`, a short word like `as` can start a line where the rewrap ends the line before with it. A line that is longer than in the rewrap must still end within `width` plus `tolerance`. This only applies to a paragraph whose only layout issue is `unbalanced`. A `widow` is still reported, and so is a paragraph that has an `orphan` hint too, so the rewrap still moves that word. A line that ends inside a link, code span, `{...}` group, or the head of a JSDoc tag is still `unbalanced` as well.
280
+ The `tolerance` also holds for each line on its own. A paragraph is not `unbalanced` when it has as many lines as the suggested rewrap, each line but the last is at most `tolerance` columns shorter than the shortest of those lines in the rewrap, and no line ends more than `tolerance` columns past `width`, unless the same line of the rewrap does too. Its suggestion then keeps the lines as they are too. The shortest line of the rewrap is the measure, not the same line, since one word that moves to another line shifts every line after it. With `width: 100` and `tolerance: 5`, a paragraph whose rewrap has lines of 92 to 98 columns passes with lines of 87 to 105 columns, the last line aside. Without a `tolerance`, a paragraph needs the exact layout of the rewrap. This only applies to a paragraph whose only layout issue is `unbalanced`, so a line that is `wide` is still reported. A `widow` is still reported, and so is a paragraph that has an `orphan` hint too, so the rewrap still moves that word. A line that ends inside a link, code span, `{...}` group, or the head of a JSDoc tag is still `unbalanced` as well.
280
281
 
281
282
  Hints are only checked in prose, never in code spans, code blocks, `@example`, or URLs. Rules that can't be measured, like writing complete sentences, are part of the agent's prompt (see `DEFAULT_RULES` in `src/comments/agent.ts`). The prompt treats the style rules as preferences, so the agent keeps punctuation that carries meaning or reads clearly, and never changes code, code spans, literals, patterns, or quotations.
282
283
 
@@ -312,7 +313,7 @@ node native/verify/verify.mjs --corpus ~/.cargo/registry/src/index.crates.io-194
312
313
 
313
314
  ### Configuration
314
315
 
315
- `width` is the preferred line length, and `maxWidth` is the hard maximum, including indentation and the comment prefix. The CLI defaults `maxWidth` to 120, or to `width` if that is larger. The wrapper may go past `width`, up to `maxWidth`, to keep a parenthetical on one line. `tolerance` (default 0) lets lines in paragraphs of three or more lines go that many columns past `width` when that saves a line. `check` also accepts a paragraph that already has the layout for a wider `width`, up to `width` plus `tolerance`, and a paragraph whose lines are each within `tolerance` columns of the suggested rewrap (see [Issues](#issues)), while the tool itself still wraps to `width`. A comment that is one paragraph stays on one line up to `singleLineWidth` plus `tolerance`, capped by `maxWidth`. A word, code span, or link longer than `maxWidth` is never split and is reported as `overflow`.
316
+ `width` is the preferred line length, and `maxWidth` is the hard maximum, including indentation and the comment prefix. The CLI defaults `maxWidth` to 120, or to `width` if that is larger. The wrapper may go past `width`, up to `maxWidth`, to keep a parenthetical on one line. `tolerance` (default 0) lets lines in paragraphs of three or more lines go that many columns past `width` when that saves a line. `check` also accepts a paragraph that already has the layout for a wider `width`, up to `width` plus `tolerance`, and a paragraph with as many lines as the suggested rewrap whose lines stay within `tolerance` columns of its shortest line and of `width` (see [Issues](#issues)), while the tool itself still wraps to `width`. A comment that is one paragraph stays on one line up to `singleLineWidth` plus `tolerance`, capped by `maxWidth`. A word, code span, or link longer than `maxWidth` is never split and is reported as `overflow`.
316
317
 
317
318
  Like prettier, `comments` looks for a config file in the current directory and its parents, up to the repository root. The config can be a `comments.config.{ts,mts,js,mjs,cjs}`, `.commentsrc`, or `.commentsrc.json` file, or a `comments` key in `package.json`. All options are optional.
318
319
 
@@ -25,7 +25,7 @@ When rules pull in different directions, the earlier one wins.
25
25
  ## Layout
26
26
 
27
27
  - After you finish, the tool rewraps prose paragraphs, list items, and JSDoc tag descriptions. The width is a preferred target. A parenthetical may extend a line up to the configured maximum so it stays together, and lines may go up to the configured tolerance past the width when that saves a line. In longer paragraphs, balanced lines take priority over keeping every parenthetical on one line. The tool also avoids short last lines where possible. Leave these line breaks to the tool, except for the sentence-orphan case below.
28
- - Don't end a line with the first word of a new sentence, clause, or parenthetical, like \`This\` after a period, \`the\` after a comma, or \`(the\`. Move it to the next line instead. The same goes for the first two words of a sentence, like \`It allocates\` after a period, when the note points at them. A long word, like an identifier or a code span, may stay at the end of a line when moving it would leave that line far shorter than the lines around it. Then leave the line breaks as they are.
28
+ - Don't end a line with the first word of a new sentence or parenthetical, like \`This\` after a period or \`(the\`, or with the first word after a semicolon or colon. Move it to the next line instead. The same goes for the first two words of a sentence, like \`It allocates\` after a period, when the note points at them. A long word, like an identifier or a code span, may stay at the end of a line when moving it would leave that line far shorter than the lines around it. Then leave the line breaks as they are. The word after a comma is no such word. A line may end with it, like \`and\` in \`...the teardown paths, and\`.
29
29
  - In a paragraph of two lines, the first line ends with a sentence whenever the rest fits on the second line and is no longer than the first. Don't end it with the start of the next sentence, like \`A wrap\` in \`...lay out differently. A wrap\`. Move those words to the second line instead.
30
30
  - A comment whose whole text is one paragraph stays on a single line when that line fits a separate, larger single-line width, even past the width. Only a longer comment wraps to the width.
31
31
  - Separate paragraphs with an empty comment line.
@@ -75,7 +75,8 @@ var ISSUE_DESCRIPTIONS = {
75
75
  underfill: "ends early, and words from the next line fit on it",
76
76
  widow: "leaves fewer than three words on the last line of its paragraph",
77
77
  unbalanced: "is in a paragraph whose lines are less even than the suggested rewrap, or that splits a link or code span that the rewrap keeps on one line",
78
- orphan: "may end with the start of a new sentence, clause, or parenthetical, which belongs on the next line (leave it if the period or comma before it is part of an abbreviation, a name, or notation like `No. 3`, or if it is a long word like an identifier and moving it would leave the line far shorter than the lines around it)",
78
+ wide: "ends more than the tolerance past the width, and past every line of the suggested rewrap",
79
+ orphan: "may end with the start of a new sentence or parenthetical, or with the first word after a semicolon or colon, which belongs on the next line (leave it if the punctuation before it is part of an abbreviation, a name, or notation like `No. 3`, or if it is a long word like an identifier and moving it would leave the line far shorter than the lines around it)",
79
80
  "split-parenthetical": "may split a parenthetical that the suggested rewrap keeps on one line, past the width but within the maximum (leave it if the split reads as well, like at a comma inside a list, and the line stays closer to the width)",
80
81
  "em-dash": "may use an em-dash to join two thoughts (leave it if it's in a quotation, a title, or a string literal, or reads more clearly than the alternatives)",
81
82
  banner: "may be a section banner (leave it if it's part of a diagram, a table, a box, a Markdown rule, a separator in an example, or legal text)",
@@ -142,7 +143,7 @@ function buildPrompt(options) {
142
143
  `Fix the formatting of the comments below, following the rules above. Paths are relative to ${options.root}. Widths include indentation and the comment prefix. The notes under each comment come from the tool's measurements, so never count columns yourself.`,
143
144
  "",
144
145
  "- After you finish, the tool rewraps every prose paragraph as described under Layout, so a layout note on a prose paragraph needs no change from you. Change only the wording there, and leave its line breaks to the tool.",
145
- "- A note that a line may end with the start of a sentence, clause, or parenthetical is the exception. Only you can tell whether it does, so if it does, move those words to the start of the next line, unless the Layout rules let a long word stay.",
146
+ "- A note that a line may end with the start of a sentence or parenthetical, or with the first word after a semicolon or colon, is the exception. Only you can tell whether it does, so if it does, move those words to the start of the next line, unless the Layout rules let a long word stay.",
146
147
  "- A note that a line may split a parenthetical is the other exception. If the parenthetical reads better on one line, move the rest of it up to the end of that line yourself.",
147
148
  "- Parts kept as written are never rewrapped, and their notes say so. Fix those notes by hand, for example by breaking an overflowing prose line or by turning a long single-line comment into a multi-line one. Keep code, code spans, tables, box drawings, hard line breaks, and other intentional layout in them exactly as they are. If a note can only be fixed by changing those, leave it and say why in your reply.",
148
149
  `- Notes marked \`${HINT_MARKER}\` come from heuristics. If the rule doesn't actually apply, don't change anything for that note.`,
@@ -430,6 +431,7 @@ function findStyleIssues(lines) {
430
431
  // src/comments/wrap.ts
431
432
  var CLAUSE_END = /[.!?,;:]["')\]*_]*$/;
432
433
  var SENTENCE_END = /[.!?]["')\]*_]*$/;
434
+ var STATEMENT_END = /[.!?;:]["')\]*_]*$/;
433
435
  var ABBREVIATIONS = /* @__PURE__ */ new Set([
434
436
  "e.g.",
435
437
  "i.e.",
@@ -661,8 +663,8 @@ function bindParentheticals(tokens, limit) {
661
663
  }
662
664
  return units;
663
665
  }
664
- function endsClause(token) {
665
- return CLAUSE_END.test(token) && !isAbbreviation(token);
666
+ function endsStatement(token) {
667
+ return STATEMENT_END.test(token) && !isAbbreviation(token);
666
668
  }
667
669
  function endsSentence(token) {
668
670
  return SENTENCE_END.test(token) && !isAbbreviation(token);
@@ -671,7 +673,7 @@ function opensAside(token) {
671
673
  return /^[([]/.test(token) && !/[)\]]["'`.,;:!?]*$/.test(token);
672
674
  }
673
675
  function isOrphan(previous, token) {
674
- return opensAside(token) || endsClause(previous) && !CLAUSE_END.test(token);
676
+ return opensAside(token) || endsStatement(previous) && !CLAUSE_END.test(token);
675
677
  }
676
678
  function startsBlock(token) {
677
679
  return /^(?:\+|-+|=+|\*+|_{3,}|\d+[.)]|#{1,6})$/.test(token) || /^(?:>|@[A-Za-z]|`{3}|~{3}|\||<(?:\/?[A-Za-z]|[!?]))/.test(token);
@@ -1072,6 +1074,7 @@ var ISSUE_TIERS = {
1072
1074
  underfill: "verdict",
1073
1075
  widow: "verdict",
1074
1076
  unbalanced: "verdict",
1077
+ wide: "verdict",
1075
1078
  orphan: "hint",
1076
1079
  "split-parenthetical": "hint",
1077
1080
  banner: "hint",
@@ -1658,6 +1661,14 @@ function analyzeParagraph(paragraph, options, language) {
1658
1661
  const restLines = layoutTokens(rest, restLimit, restLimit, restOptions);
1659
1662
  return sameLayout(breakAfterSentence(restLines, restLimit, restLimit, restOptions), currentLines.slice(line + 1));
1660
1663
  };
1664
+ const differenceAt = (line) => {
1665
+ const longest = Math.max(...wrapped.map((words) => words.join(" ").length));
1666
+ const wide = currentLines.flatMap((words, position) => {
1667
+ const limit = (position === 0 ? firstLimit : restLimit) + layoutOptions.tolerance;
1668
+ return words.join(" ").length > Math.max(limit, longest) ? [{ type: "wide", line: position }] : [];
1669
+ });
1670
+ return wide.length > 0 ? wide : [{ type: "unbalanced", line }];
1671
+ };
1661
1672
  if (overflow.length === 0 && (currentLines.length === wrapped.length || options.maxWidth !== void 0)) {
1662
1673
  const line = Math.max(
1663
1674
  wrapped.findIndex((words, position) => words.join(" ") !== currentLines[position]?.join(" ")),
@@ -1666,9 +1677,11 @@ function analyzeParagraph(paragraph, options, language) {
1666
1677
  if (lineIssues.length === 0 && (sameLayout(even, currentLines) || keepsOnlySentenceStarts())) {
1667
1678
  lineIssues.push({ type: "orphan", line });
1668
1679
  } else if (lineIssues.length === 0) {
1669
- lineIssues.push({ type: splitsOnlyParenthetical(line) ? "split-parenthetical" : "unbalanced", line });
1680
+ lineIssues.push(
1681
+ ...splitsOnlyParenthetical(line) ? [{ type: "split-parenthetical", line }] : differenceAt(line)
1682
+ );
1670
1683
  } else if (lineIssues.every((issue) => issue.type === "orphan") && !sameLayout(even, currentLines) && !keepsOnlyOrphans()) {
1671
- lineIssues.push({ type: "unbalanced", line });
1684
+ lineIssues.push(...differenceAt(line));
1672
1685
  }
1673
1686
  }
1674
1687
  const splitLine = splitTokenLine(currentLines, tokens);
@@ -1686,14 +1699,15 @@ function analyzeParagraph(paragraph, options, language) {
1686
1699
  return false;
1687
1700
  };
1688
1701
  const staysNearLayout = () => {
1689
- if (currentLines.length !== wrapped.length || splitTokenLine(currentLines, units) !== -1) {
1702
+ if (layoutOptions.tolerance === 0 || currentLines.length !== wrapped.length || splitTokenLine(currentLines, units) !== -1) {
1690
1703
  return false;
1691
1704
  }
1705
+ const shortest = Math.min(...wrapped.slice(0, -1).map((words) => words.join(" ").length));
1692
1706
  return currentLines.every((words, position) => {
1693
1707
  const length = words.join(" ").length;
1694
- const suggestedLength = wrapped[position].join(" ").length;
1695
1708
  const limit = (position === 0 ? firstLimit : restLimit) + layoutOptions.tolerance;
1696
- return Math.abs(length - suggestedLength) <= layoutOptions.tolerance && length <= Math.max(suggestedLength, limit);
1709
+ const isLast = position === currentLines.length - 1;
1710
+ return (isLast || length >= shortest - layoutOptions.tolerance) && length <= Math.max(wrapped[position].join(" ").length, limit);
1697
1711
  });
1698
1712
  };
1699
1713
  const hasVerdict = lineIssues.some((issue) => isVerdict(issue));