-
-
Notifications
You must be signed in to change notification settings - Fork 165
Add lines alignment rule #636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
e20942f
Add check-lines-alignment rule
renatho 8a407dc
Add check-lines-alignment documentation
renatho 82e2cf2
Remove unused argument
renatho c36805e
Fix jsdoc position
renatho 3461640
Add WordPress recomendation as example for check-lines-alignment rule
renatho a50a00d
Fix check-lines-alignment documentation title
renatho 98ae62c
Refactor create sequence function
renatho af433fa
Add todo comment to remove method when String.prototype.matchAll is s…
renatho 65512e9
Add options to check lines alignment rule
renatho 771d550
Refactor spaces repeat
renatho 38b8b9d
Refactor not implemented option warning
renatho 1c1dd68
Update check-lines-alignmenment rule to off as recommended
renatho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
### `check-lines-alignment` | ||
|
||
Reports invalid alignment of JSDoc block lines. This is a | ||
[standard recommended to WordPress code](https://make.wordpress.org/core/handbook/best-practices/inline-documentation-standards/javascript/#aligning-comments), for example. | ||
|
||
#### Options | ||
|
||
This rule allows one optional string argument. If it is `"always"` then a | ||
problem is raised when the lines are not aligned. If it is `"never"` then | ||
a problem should be raised when there is more than one space between the | ||
lines parts. Only the non-default `"always"` is implemented for now. | ||
|
||
||| | ||
|---|---| | ||
|Context|everywhere| | ||
|Options|(a string matching `"always"|"never"`)| | ||
|Tags|`param`, `arg`, `argument`, `property`, `prop`| | ||
|
||
The following patterns are considered problems: | ||
|
||
````js | ||
/** | ||
* Function description. | ||
* | ||
* @param {string} lorem Description. | ||
* @param {int} sit Description multi words. | ||
*/ | ||
const fn = ( lorem, sit ) => {} | ||
// Options: ["always"] | ||
// Message: Expected JSDoc block lines to be aligned. | ||
|
||
/** | ||
* My object. | ||
* | ||
* @typedef {Object} MyObject | ||
* | ||
* @property {string} lorem Description. | ||
* @property {int} sit Description multi words. | ||
*/ | ||
// Options: ["always"] | ||
// Message: Expected JSDoc block lines to be aligned. | ||
|
||
The following patterns are not considered problems: | ||
|
||
````js | ||
/** | ||
* Function description. | ||
* | ||
* @param {string} lorem Description. | ||
* @param {int} sit Description multi words. | ||
*/ | ||
const fn = ( lorem, sit ) => {} | ||
// Options: ["always"] | ||
|
||
/** | ||
* My object. | ||
* | ||
* @typedef {Object} MyObject | ||
* | ||
* @property {string} lorem Description. | ||
* @property {int} sit Description multi words. | ||
*/ | ||
// Options: ["always"] | ||
```` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
import { | ||
set, | ||
} from 'lodash'; | ||
import iterateJsdoc from '../iterateJsdoc'; | ||
|
||
/** | ||
* Aux method until we consider the dev envs support `String.prototype.matchAll` (Node 12+). | ||
* | ||
brettz9 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @param {string} string String that will be checked. | ||
* @param {RegExp} regexp Regular expression to run. | ||
* @param {Function} callback Function to be called each iteration. | ||
* @param {int} limit Limit of matches that we want to exec. | ||
* | ||
* @todo [engine:node@>=12]: Remove function and use `String.prototype.matchAll` instead. | ||
*/ | ||
const matchAll = (string, regexp, callback, limit) => { | ||
let result; | ||
let index = 0; | ||
|
||
while ((result = regexp.exec(string)) && index <= limit - 1) { | ||
// eslint-disable-next-line promise/prefer-await-to-callbacks | ||
callback(result, index++); | ||
} | ||
}; | ||
|
||
/** | ||
* Get the full description from a line. | ||
* | ||
* @param {string} lineString The line string. | ||
* | ||
* @returns {string} The full description. | ||
*/ | ||
const getFullDescription = (lineString) => { | ||
return /(?:\S+\s+){4}(.*)/.exec(lineString)[1]; | ||
}; | ||
|
||
/** | ||
* Get the expected positions for each part. | ||
* | ||
* @param {int[]} partsMaxLength Max length of each part. | ||
* @param {int} indentLevel JSDoc indent level. | ||
* | ||
* @returns {int[]} Expected position for each part. | ||
*/ | ||
const getExpectedPositions = (partsMaxLength, indentLevel) => { | ||
// eslint-disable-next-line unicorn/no-reduce | ||
return partsMaxLength.reduce( | ||
(acc, cur, index) => { | ||
return [...acc, cur + acc[index] + 1]; | ||
}, | ||
[indentLevel], | ||
); | ||
}; | ||
|
||
/** | ||
* Check is not aligned. | ||
* | ||
* @param {int[]} expectedPositions Expected position for each part. | ||
* @param {Array[]} partsMatrix Parts matrix. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
const isNotAligned = (expectedPositions, partsMatrix) => { | ||
return partsMatrix.some((line) => { | ||
return line.some( | ||
({position}, partIndex) => { | ||
return position !== expectedPositions[partIndex]; | ||
}, | ||
); | ||
}); | ||
}; | ||
|
||
/** | ||
* Fix function creator for the report. It creates a function which fix | ||
* the JSDoc with the correct alignment. | ||
* | ||
* @param {object} comment Comment node. | ||
* @param {int[]} expectedPositions Array with the expected positions. | ||
* @param {Array[]} partsMatrix Parts matrix. | ||
* @param {RegExp} lineRegExp Line regular expression. | ||
* @param {string} tagIndentation Tag indentation. | ||
* | ||
* @returns {Function} Function which fixes the JSDoc alignment. | ||
*/ | ||
const createFixer = (comment, expectedPositions, partsMatrix, lineRegExp, tagIndentation) => { | ||
return (fixer) => { | ||
let lineIndex = 0; | ||
|
||
// Replace every line with the correct spacings. | ||
const fixed = comment.value.replace(lineRegExp, () => { | ||
// eslint-disable-next-line unicorn/no-reduce | ||
return partsMatrix[lineIndex++].reduce( | ||
(acc, {string}, index) => { | ||
const spacings = ' '.repeat(expectedPositions[index] - acc.length); | ||
|
||
return acc + (index === 0 ? tagIndentation : spacings) + string; | ||
}, | ||
'', | ||
); | ||
}); | ||
|
||
return fixer.replaceText(comment, '/*' + fixed + '*/'); | ||
}; | ||
}; | ||
|
||
/** | ||
* Check comment per tag. | ||
* | ||
* @param {object} comment Comment node. | ||
* @param {string} tag Tag string. | ||
* @param {string} tagIndentation Tag indentation. | ||
* @param {Function} report Report function. | ||
*/ | ||
const checkCommentPerTag = (comment, tag, tagIndentation, report) => { | ||
const lineRegExp = new RegExp(`.*@${tag}[\\s].*`, 'gm'); | ||
const lines = comment.value.match(lineRegExp); | ||
|
||
if (!lines) { | ||
return; | ||
} | ||
|
||
/** | ||
* A matrix containing the current position and the string of each part for each line. | ||
* 0 - Asterisk. | ||
* 1 - Tag. | ||
* 2 - Type. | ||
* 3 - Variable name. | ||
* 4 - Description (Optional). | ||
*/ | ||
const partsMatrix = []; | ||
|
||
/** | ||
* The max length of each part, comparing all the lines. | ||
*/ | ||
const partsMaxLength = []; | ||
|
||
// Loop (lines x parts) to populate partsMatrix and partsMaxLength. | ||
lines.forEach((lineString, lineIndex) => { | ||
// All line parts until the first word of the description (if description exists). | ||
matchAll( | ||
lineString, | ||
/\S+/g, | ||
({0: match, index: position}, partIndex) => { | ||
set(partsMatrix, [lineIndex, partIndex], { | ||
position, | ||
string: partIndex === 4 ? getFullDescription(lineString) : match, | ||
}); | ||
|
||
const partLength = match.length; | ||
const maxLength = partsMaxLength[partIndex]; | ||
|
||
partsMaxLength[partIndex] = maxLength > partLength ? maxLength : partLength; | ||
}, | ||
5, | ||
); | ||
}); | ||
|
||
const expectedPositions = getExpectedPositions(partsMaxLength, tagIndentation.length); | ||
|
||
if (isNotAligned(expectedPositions, partsMatrix)) { | ||
report( | ||
'Expected JSDoc block lines to be aligned.', | ||
createFixer( | ||
comment, | ||
expectedPositions, | ||
partsMatrix, | ||
lineRegExp, | ||
tagIndentation, | ||
), | ||
); | ||
} | ||
}; | ||
|
||
export default iterateJsdoc(({ | ||
jsdocNode, | ||
report, | ||
context, | ||
indent, | ||
}) => { | ||
if (context.options[0] === 'never') { | ||
report('The `never` option is not yet implemented for this rule.'); | ||
|
||
return; | ||
} | ||
|
||
if (context.options[0] !== 'always') { | ||
return; | ||
} | ||
|
||
// `indent` is whitespace from line 1 (`/**`), so slice and account for "/". | ||
const tagIndentation = indent + ' '; | ||
|
||
['param', 'arg', 'argument', 'property', 'prop'].forEach((tag) => { | ||
checkCommentPerTag(jsdocNode, tag, tagIndentation, report); | ||
}); | ||
}, { | ||
iterateAllJsdocs: true, | ||
meta: { | ||
docs: { | ||
description: 'Reports invalid alignment of JSDoc block lines.', | ||
url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-check-lines-alignment', | ||
}, | ||
fixable: 'whitespace', | ||
schema: [ | ||
{ | ||
enum: ['always', 'never'], | ||
type: 'string', | ||
}, | ||
], | ||
type: 'layout', | ||
}, | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.