Skip to content

Commit

Permalink
Merge branch 'feat/custom-test-expressions' into master
Browse files Browse the repository at this point in the history
Close GH-116
  • Loading branch information
zackad committed Feb 1, 2025
2 parents 5313e32 + 555602f commit 02b752c
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features
- [GH-61](https://github.com/zackad/prettier-plugin-twig/issues/61#issuecomment-2596726423) Add support for twig comment as html element attribute
- [GH-116](https://github.com/zackad/prettier-plugin-twig/issues/116) Add possibility to register any custom Twig test expression

### Bugfixes
- Fix broken embed statement with `only` modifier
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,25 @@ If we did not list the `"nav,endnav"` entry in `twigMultiTags`, this code exampl

Note that the order matters: It has to be `"nav,endnav"`, and it must not be `"endnav,nav"`. In general, the first and the last tag name matter. In the case of `"switch,case,default,endswitch"`, the order of `case` and `default` does not matter. However, `switch` has to come first, and `endswitch` has to come last.

### twigTestExpressions (default: `[]`)

Make custom Twig tests known to the parser.

```json
twigTestExpressions: [
"snake_case_test",
"camelCaseTest"
]
```

__Example__
```twig
{{ a is snake_case_test }}
{{ a is not snake_case_test }}
{{ a is camelCaseTest }}
{{ a is not camelCaseTest }}
```

## Features

### `prettier-ignore` and `prettier-ignore-start`
Expand Down
7 changes: 7 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ const printers = {

/** @type {import('prettier').Options} Options */
const options = {
twigTestExpressions: {
type: "path",
category: "Global",
array: true,
default: [{ value: [] }],
description: "Make custom Twig tests known to the parser."
},
twigMultiTags: {
type: "path",
category: "Global",
Expand Down
35 changes: 23 additions & 12 deletions src/melody/melody-extension-core/operators.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,13 +301,19 @@ export const BinaryEmptyCoalesceExpression = createBinaryOperatorNode({
//endregion

//region Test Expressions
export const TestEvenExpression = createTest("even", "TestEvenExpression");
export const TestOddExpression = createTest("odd", "TestOddExpression");
export const TestDefinedExpression = createTest(
export const TestEvenExpression = createTestExpression(
"even",
"TestEvenExpression"
);
export const TestOddExpression = createTestExpression(
"odd",
"TestOddExpression"
);
export const TestDefinedExpression = createTestExpression(
"defined",
"TestDefinedExpression"
);
export const TestSameAsExpression = createTest(
export const TestSameAsExpression = createTestExpression(
"same as",
"TestSameAsExpression"
);
Expand All @@ -318,14 +324,17 @@ tests.push({
return new TestSameAsExpression(expr, args);
}
});
export const TestNullExpression = createTest("null", "TestNullExpression");
export const TestNullExpression = createTestExpression(
"null",
"TestNullExpression"
);
tests.push({
text: "none",
createNode(expr, args) {
return new TestNullExpression(expr, args);
}
});
export const TestDivisibleByExpression = createTest(
export const TestDivisibleByExpression = createTestExpression(
"divisible by",
"TestDivisibleByExpression"
);
Expand All @@ -336,24 +345,26 @@ tests.push({
return new TestDivisibleByExpression(expr, args);
}
});
export const TestConstantExpression = createTest(
export const TestConstantExpression = createTestExpression(
"constant",
"TestConstantExpression"
);
export const TestEmptyExpression = createTest("empty", "TestEmptyExpression");
export const TestIterableExpression = createTest(
export const TestEmptyExpression = createTestExpression(
"empty",
"TestEmptyExpression"
);
export const TestIterableExpression = createTestExpression(
"iterable",
"TestIterableExpression"
);

export const TestInstanceOfExpression = createTest(
export const TestInstanceOfExpression = createTestExpression(
"instance of",
"TestInstanceOfExpression"
);
//endregion

//region Utilities
function createTest(text, typeName) {
export function createTestExpression(text, typeName) {
const TestExpression = class extends Node {
/**
* @param {Node} expr
Expand Down
8 changes: 8 additions & 0 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Parser
} from "./melody/melody-parser/index.js";
import { extension as coreExtension } from "./melody/melody-extension-core/index.js";
import { createTestExpression } from "./melody/melody-extension-core/operators.js";

const ORIGINAL_SOURCE = Symbol("ORIGINAL_SOURCE");

Expand Down Expand Up @@ -74,8 +75,15 @@ const getMultiTagConfig = (tagsCsvs = []) =>
return acc;
}, {});

const registerTestExtensions = (testExpressions = []) => {
testExpressions.forEach(test =>
createTestExpression(test, `Test[${test}]Expression`)
);
};

const parse = (text, parsers, options) => {
const multiTagConfig = getMultiTagConfig(options.twigMultiTags || []);
registerTestExtensions(options.twigTestExpressions || []);

const extensions = [coreExtension];
const parser = createConfiguredParser(text, multiTagConfig, ...extensions);
Expand Down
10 changes: 9 additions & 1 deletion src/print/TestExpression.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ const printTestExpression = (node, path, print) => {
if (isNegator(parent)) {
parts.push("not ");
}
if (!textMap[expressionType]) {

if (expressionType.includes("[") && expressionType.includes("]")) {
const testText = expressionType.substring(
expressionType.indexOf("[") + 1,
expressionType.lastIndexOf("]")
);

parts.push(testText);
} else if (!textMap[expressionType]) {
console.error(
"TestExpression: No text for " + expressionType + " defined"
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{ a is snake_case_test }}
{{ a is not snake_case_test }}
{{ a is camelCaseTest }}
{{ a is not camelCaseTest }}
4 changes: 4 additions & 0 deletions tests/Expressions/custom_test_expression.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{ a is snake_case_test }}
{{ a is not snake_case_test }}
{{ a is camelCaseTest }}
{{ a is not camelCaseTest }}
9 changes: 9 additions & 0 deletions tests/Expressions/jsfmt.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,13 @@ describe("Expressions", () => {
});
await expect(actual).toMatchFileSnapshot(snapshotFile);
});
it("should handle custom test expressions", async () => {
const { actual, snapshotFile } = await run_spec(import.meta.url, {
source: "custom_test_expression.twig",
formatOptions: {
twigTestExpressions: ["snake_case_test", "camelCaseTest"]
}
});
await expect(actual).toMatchFileSnapshot(snapshotFile);
});
});

0 comments on commit 02b752c

Please sign in to comment.