Skip to content

Commit

Permalink
feat: add array.items-linebreak rule
Browse files Browse the repository at this point in the history
  • Loading branch information
abdul-alhasany committed Jul 27, 2024
1 parent b7de12a commit 6aecaf6
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "taqwim",
"displayName": "PHPTaqwim",
"description": "PHP linter and formatter",
"version": "0.0.63",
"version": "0.0.64",
"homepage": "https://taqwim.kalimah-apps.com/",
"repository": {
"type": "git",
Expand Down
17 changes: 17 additions & 0 deletions schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,23 @@
}
]
},
"taqwim/array.items-linebreak": {
"description": "Break array into multiple lines or group them into a single line based on the position of the first item",
"oneOf": [
{
"$ref": "#/definitions/severity"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"severity": {
"$ref": "#/definitions/severity"
}
}
}
]
},
"taqwim/array.syntax": {
"description": "Ensure that array syntax is consistent",
"oneOf": [
Expand Down
2 changes: 1 addition & 1 deletion taqwim/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@kalimahapps/taqwim",
"description": "PHP linter and formatter",
"author": "khr2003",
"version": "0.0.63",
"version": "0.0.64",
"homepage": "https://taqwim.kalimah-apps.com/docs/",
"repository": {
"type": "git",
Expand Down
183 changes: 183 additions & 0 deletions taqwim/src/rules/array/items-linebreak.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
* Break array items into multiple lines or group them into a single line
* based on the position of the first item
*/
import type { AstArray, RuleContext, RuleDataOptional } from '@taqwim/types';
class ArrayItemsLineBreak {
/**
* Rule context
*/
context = {} as RuleContext;

/**
* Break array items into multiple lines
*
* @param {AstArray['items']} items Array items to process
*/
reportAndFixBreaking(items: AstArray['items']) {
const { report, node } = this.context;
const { loc: arrayLoc } = node as AstArray;

for (const [index, item] of items.entries()) {
const { loc } = item;
const nextItem = items[index + 1];
let endLoc = nextItem?.loc.start;
if (endLoc === undefined) {
// -1 to remove location of ) or ] from the end of the array
endLoc = {
line: arrayLoc.end.line,
column: arrayLoc.end.column - 1,
offset: arrayLoc.end.offset - 1,
};
}

// If current item and the next item are not on the same line
// then there is no need to add a line break
if (loc.end.line !== endLoc.line) {
continue;
}

const fixPoisition = {
start: loc.end,
end: endLoc,
};

report({
message: 'There should be a line break after this item.',
position: {
start: {
line: loc.end.line,
column: loc.end.column - 2,
offset: loc.end.offset - 2,
},
end: {
line: loc.end.line,
column: loc.end.column,
offset: loc.end.offset,
},
},
fix(fixer) {
return fixer.after(fixPoisition, '\n');
},
});
}
}

/**
* Group array items into a single line
*
* @param {AstArray['items']} items Array items to process
*/
reportAndFixGrouping(items: AstArray['items']) {
const { report, sourceLines, node, sourceCode } = this.context;
const { loc: arrayLoc } = node as AstArray;
for (const [index, item] of items.entries()) {
const { loc } = item;
const nextItem = items[index + 1];
let endLoc = nextItem?.loc.start;
if (endLoc === undefined) {
// -1 to remove location of ) or ] from the end of the array
endLoc = {
line: arrayLoc.end.line,
column: arrayLoc.end.column - 1,
offset: arrayLoc.end.offset - 1,
};
}

if (loc.end.line === endLoc.line) {
continue;
}

const lastLine = sourceLines[loc.end.line];
const postItemContent = lastLine.slice(loc.end.column);

// Check for comma after the item, because last item might not
// have a dangling comma
const hasComma = postItemContent.match(/\s*,/u);
const commaIndex = hasComma?.index === undefined ? 0 : hasComma.index + 1;

const fixerPosition = {
start: {
line: loc.end.line,
column: loc.end.column + commaIndex,
offset: loc.end.offset + commaIndex,
},
end: endLoc,
};

// Make sure that the content removed in fixer is only whitespace
const contentToReplace = sourceCode.slice(
fixerPosition.start.offset,
fixerPosition.end.offset
);
const isOnlyWhitespace = contentToReplace.trim() === '';
if (!isOnlyWhitespace) {
continue;
}
report({
message: 'There should not be a line break after this item.',

// Show the message at the end of the item
position: {
start: {
line: loc.end.line,
column: loc.end.column - 2,
offset: loc.end.offset - 2,
},
end: {
line: loc.end.line,
column: loc.end.column + commaIndex,
offset: loc.end.offset + commaIndex,
},
},
fix(fixer) {
return fixer.replaceRange(fixerPosition, ' ');
},
});
}
}

/**
* Process the rule
*
* @param {RuleContext} context Rule context
*/
process (context: RuleContext) {
this.context = context;
const { node } = context;
const { items, loc: arrayLoc } = node as AstArray;

if (items.length === 0) {
return;
}

const firstItem = items[0];
const isSameLine = arrayLoc.start.line === firstItem.loc.start.line;

if (isSameLine !== true) {
this.reportAndFixBreaking(items);
return;
}

// If array start and end lines are on the same line
// then there is no need to group them into a single line
// as it will be a single line array
if (arrayLoc.start.line === arrayLoc.end.line) {
return;
}
this.reportAndFixGrouping(items);
}
}

export default (): RuleDataOptional => {
return {
meta: {
description: 'Break array items into multiple lines or group them into a single line based on the position of the first item',
fixable: true,
preset: 'taqwim',
},
name: 'array.items-linebreak',
register: ['array'],
bindClass: ArrayItemsLineBreak,
};
};
1 change: 1 addition & 0 deletions taqwim/src/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export { default as propertyLimit } from '@taqwim/rules/property-limit.js';
export { default as propertyNoVar } from '@taqwim/rules/property-no-var.js';
export { default as typeCheck } from '@taqwim/rules/type-check.js';
export { default as arrayCommaDangle } from '@taqwim/rules/array/comma-dangle.js';
export { default as arrayItemsLinebreak } from '@taqwim/rules/array/items-linebreak.js';
export { default as arraySyntax } from '@taqwim/rules/array/syntax.js';
export { default as methodBan } from '@taqwim/rules/method/ban.js';
export { default as methodBreakParameters } from '@taqwim/rules/method/break-parameters.js';
Expand Down
2 changes: 1 addition & 1 deletion taqwim/src/utils/find-ahead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const findAhead = (
*
* @param {RegExpMatchArray} match The match to get the groups from
* @param {Loc} position The position of the entire match
* @param {number} columnAdjust How much to adjust the column with based on
* @param {number} columnAdjust How much to adjust the column with based on
* the start of the match.
* @return {MatchGroupType} The groups with location
*/
Expand Down
42 changes: 42 additions & 0 deletions taqwim/test/rules/array.items-linebreak/correct-1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
$months = array(
"january",
"february",
"march",
"april",
"may",
"june",
"july",
"august",
"september",
"october",
"november",
"december",
);

$seasons = array(
"summer",
"winter",
"spring",
"autumn",
);

$array = array(
"foo" => "bar",
"bar" => "foo",
100 => -100,
-100 => 100,
);

$bigone = [
'name' => 'bigone',
'children' => [
'1a' => 'child',
'11b' => 'child',
'111c' => 'child',
'children' => [
'child' => 'aaa',
],
],
'short_name' => 'big'
];
5 changes: 5 additions & 0 deletions taqwim/test/rules/array.items-linebreak/correct-2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
$array = array("foo", "bar", "hello", "world");
$seasons = array("summer","winter","spring","autumn");
$months = array("january","february","march","april","may","june","july","august","september","october","november","december");
$args = array('"' . $this->id . '"', (int) $has_sessions,);
16 changes: 16 additions & 0 deletions taqwim/test/rules/array.items-linebreak/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"default": {
"correct-1": {
"description": "Array items are on separate lines",
"expected": 0
},
"correct-2": {
"description": "Arrays items are on the same line",
"expected": 0
},
"incorrect-1": {
"description": "Array items do not have consistent line breaks",
"expected": 16
}
}
}
37 changes: 37 additions & 0 deletions taqwim/test/rules/array.items-linebreak/fixer/1-from.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
$months = array( "january",
"february",
"march", "april",
"may",
"june", "july", "august", "september",
"october",
"november", "december",
);

$seasons = array(
"summer", "winter",
"spring", "autumn",
);

$seasons = array("summer",
// Comment, fixer should add a line break here
"winter", "spring",
"autumn",
"fall",
"one",
"two",
);

$array = array( "foo" => "bar",
"bar" => "foo",
100 => -100, -100 => 100,
);

$bigone = ['name' => 'bigone',
'children' => [
'1a' => 'child', '11b' => 'child',
'111c' => 'child',
'children' => [ 'child' => 'aaa',
],
],'short_name' => 'big'
];
22 changes: 22 additions & 0 deletions taqwim/test/rules/array.items-linebreak/fixer/1-to.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
$months = array( "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december", );

$seasons = array(
"summer",
"winter",
"spring",
"autumn",
);

$seasons = array("summer",
// Comment, fixer should add a line break here
"winter", "spring", "autumn", "fall", "one", "two", );

$array = array( "foo" => "bar", "bar" => "foo", 100 => -100, -100 => 100, );

$bigone = ['name' => 'bigone', 'children' => [
'1a' => 'child',
'11b' => 'child',
'111c' => 'child',
'children' => [ 'child' => 'aaa', ],
],'short_name' => 'big' ];
Loading

0 comments on commit 6aecaf6

Please sign in to comment.