Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
Add support for the paginate tag
Browse files Browse the repository at this point in the history
Fixes #61
  • Loading branch information
charlespwd committed Aug 12, 2022
1 parent fcdf9e8 commit 29874ff
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 4 deletions.
9 changes: 7 additions & 2 deletions grammar/liquid-html.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ LiquidHTML {

liquidTagOpen =
| liquidTagOpenForm
| liquidTagOpenPaginate
| liquidTagOpenBaseCase
liquidTagClose = "{%" "-"? space* "end" blockName space* tagMarkup "-"? "%}"
liquidTag =
Expand Down Expand Up @@ -107,17 +108,20 @@ LiquidHTML {
liquidTagInclude = liquidTagRule<"include", liquidTagRenderMarkup>
liquidTagRender = liquidTagRule<"render", liquidTagRenderMarkup>
liquidTagRenderMarkup =
snippetExpression renderVariableExpression? renderAliasExpression? (argumentSeparatorOptionalComma renderArguments) space*
snippetExpression renderVariableExpression? renderAliasExpression? (argumentSeparatorOptionalComma tagArguments) space*
snippetExpression = liquidString | variableSegmentAsLookup
renderVariableExpression = space+ ("for" | "with") space+ liquidExpression
renderAliasExpression = space+ "as" space+ variableSegment
renderArguments = listOf<namedArgument, argumentSeparatorOptionalComma>

liquidTagOpenBaseCase = liquidTagOpenRule<blockName, tagMarkup>

liquidTagOpenForm = liquidTagOpenRule<"form", liquidTagOpenFormMarkup>
liquidTagOpenFormMarkup = arguments space*

liquidTagOpenPaginate = liquidTagOpenRule<"paginate", liquidTagOpenPaginateMarkup>
liquidTagOpenPaginateMarkup =
liquidExpression space+ "by" space+ liquidExpression (argumentSeparatorOptionalComma tagArguments)? space*

liquidDrop = "{{" "-"? space* liquidDropCases "-"? "}}"
liquidDropCases = liquidVariable | liquidDropBaseCase
liquidDropBaseCase = anyExceptStar<("-}}" | "}}")>
Expand Down Expand Up @@ -186,6 +190,7 @@ LiquidHTML {
argumentSeparatorOptionalComma = space* ","? space*
positionalArgument = liquidExpression ~(space* ":")
namedArgument = variableSegment space* ":" space* liquidExpression
tagArguments = listOf<namedArgument, argumentSeparatorOptionalComma>

variableSegment = (letter | "_") identifierCharacter*
variableSegmentAsLookup = variableSegment
Expand Down
39 changes: 39 additions & 0 deletions src/parser/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
ConcreteLiquidTagOpenNamed,
ConcreteLiquidTagOpen,
ConcreteLiquidArgument,
ConcretePaginateMarkup,
} from '~/parser/cst';
import { isLiquidHtmlNode, NamedTags, NodeTypes, Position } from '~/types';
import { assertNever, deepGet, dropLast } from '~/utils';
Expand All @@ -50,6 +51,7 @@ export type LiquidHtmlNode =
| LiquidNamedArgument
| AssignMarkup
| RenderMarkup
| PaginateMarkup
| RenderVariableExpression
| TextNode;

Expand Down Expand Up @@ -106,6 +108,7 @@ export type LiquidTagNamed =
| LiquidTagEcho
| LiquidTagForm
| LiquidTagInclude
| LiquidTagPaginate
| LiquidTagRender
| LiquidTagSection;

Expand Down Expand Up @@ -142,6 +145,15 @@ export interface AssignMarkup extends ASTNode<NodeTypes.AssignMarkup> {

export interface LiquidTagForm
extends LiquidTagNode<NamedTags.form, LiquidArgument[]> {}

export interface LiquidTagPaginate
extends LiquidTagNode<NamedTags.paginate, PaginateMarkup> {}
export interface PaginateMarkup extends ASTNode<NodeTypes.PaginateMarkup> {
collection: LiquidExpression;
pageSize: LiquidExpression;
args: LiquidNamedArgument[];
}

export interface LiquidTagRender
extends LiquidTagNode<NamedTags.render, RenderMarkup> {}
export interface LiquidTagInclude
Expand Down Expand Up @@ -730,13 +742,15 @@ function toNamedLiquidTag(
...liquidTagBaseAttributes(node, source),
};
}

case NamedTags.assign: {
return {
name: NamedTags.assign,
markup: toAssignMarkup(node.markup, source),
...liquidTagBaseAttributes(node, source),
};
}

case NamedTags.include:
case NamedTags.render: {
return {
Expand All @@ -745,13 +759,15 @@ function toNamedLiquidTag(
...liquidTagBaseAttributes(node, source),
};
}

case NamedTags.section: {
return {
name: node.name,
markup: toExpression(node.markup, source) as LiquidString,
...liquidTagBaseAttributes(node, source),
};
}

case NamedTags.form: {
return {
name: node.name,
Expand All @@ -761,6 +777,15 @@ function toNamedLiquidTag(
};
}

case NamedTags.paginate: {
return {
name: node.name,
markup: toPaginateMarkup(node.markup, source),
children: [],
...liquidTagBaseAttributes(node, source),
};
}

default: {
return assertNever(node);
}
Expand All @@ -780,6 +805,20 @@ function toAssignMarkup(
};
}

function toPaginateMarkup(
node: ConcretePaginateMarkup,
source: string,
): PaginateMarkup {
return {
type: NodeTypes.PaginateMarkup,
collection: toExpression(node.collection, source),
pageSize: toExpression(node.pageSize, source),
position: position(node),
args: node.args ? node.args.map((arg) => toNamedArgument(arg, source)) : [],
source,
};
}

function toRenderMarkup(
node: ConcreteLiquidTagRenderMarkup,
source: string,
Expand Down
45 changes: 45 additions & 0 deletions src/parser/cst.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,51 @@ describe('Unit: toLiquidHtmlCST(text)', () => {
expectLocation(cst, '0');
});
});

it('should parse the paginate tag open markup as arguments', () => {
[
{
expression: `collection.products by 50`,
collection: { type: 'VariableLookup' },
pageSize: { type: 'Number' },
},
{
expression: `collection.products by setting.value`,
collection: { type: 'VariableLookup' },
pageSize: { type: 'VariableLookup' },
},
{
expression: `collection.products by setting.value window_size: 2`,
collection: { type: 'VariableLookup' },
pageSize: { type: 'VariableLookup' },
args: [{ type: 'Number' }],
},
{
expression: `collection.products by setting.value, window_size: 2`,
collection: { type: 'VariableLookup' },
pageSize: { type: 'VariableLookup' },
args: [{ type: 'Number' }],
},
].forEach(({ expression, collection, pageSize, args }) => {
cst = toLiquidHtmlCST(`{% paginate ${expression} -%}`);
expectPath(cst, '0.type').to.equal('LiquidTagOpen');
expectPath(cst, '0.name').to.equal('paginate');
expectPath(cst, '0.markup.type').to.equal('PaginateMarkup');
expectPath(cst, '0.markup.collection.type').to.equal(collection.type);
expectPath(cst, '0.markup.pageSize.type').to.equal(pageSize.type);
if (args) {
expectPath(cst, '0.markup.args').to.have.lengthOf(args.length);
args.forEach((arg, i) => {
expectPath(cst, `0.markup.args.${i}.type`).to.equal('NamedArgument');
expectPath(cst, `0.markup.args.${i}.value.type`).to.equal(arg.type);
});
} else {
expectPath(cst, '0.markup.args').to.have.lengthOf(0);
}
expectLocation(cst, '0');
expectLocation(cst, '0.markup');
});
});
});

describe('Case: LiquidNode', () => {
Expand Down
29 changes: 27 additions & 2 deletions src/parser/cst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export enum ConcreteNodeTypes {

AssignMarkup = 'AssignMarkup',
RenderMarkup = 'RenderMarkup',
PaginateMarkup = 'PaginateMarkup',
RenderVariableExpression = 'RenderVariableExpression',
}

Expand Down Expand Up @@ -137,7 +138,9 @@ export interface ConcreteLiquidRawTag
export type ConcreteLiquidTagOpen =
| ConcreteLiquidTagOpenBaseCase
| ConcreteLiquidTagOpenNamed;
export type ConcreteLiquidTagOpenNamed = ConcreteLiquidTagOpenForm;
export type ConcreteLiquidTagOpenNamed =
| ConcreteLiquidTagOpenForm
| ConcreteLiquidTagOpenPaginate;

export interface ConcreteLiquidTagOpenNode<Name, Markup>
extends ConcreteBasicLiquidNode<ConcreteNodeTypes.LiquidTagOpen> {
Expand All @@ -151,6 +154,19 @@ export interface ConcreteLiquidTagOpenBaseCase
export interface ConcreteLiquidTagOpenForm
extends ConcreteLiquidTagOpenNode<NamedTags.form, ConcreteLiquidArgument[]> {}

export interface ConcreteLiquidTagOpenPaginate
extends ConcreteLiquidTagOpenNode<
NamedTags.paginate,
ConcretePaginateMarkup
> {}

export interface ConcretePaginateMarkup
extends ConcreteBasicNode<ConcreteNodeTypes.PaginateMarkup> {
collection: ConcreteLiquidExpression;
pageSize: ConcreteLiquidExpression;
args: ConcreteLiquidNamedArgument[] | null;
}

export interface ConcreteLiquidTagClose
extends ConcreteBasicLiquidNode<ConcreteNodeTypes.LiquidTagClose> {
name: string;
Expand Down Expand Up @@ -447,6 +463,15 @@ export function toLiquidHtmlCST(text: string): LiquidHtmlCST {

liquidTagOpenForm: 0,
liquidTagOpenFormMarkup: 0,
liquidTagOpenPaginate: 0,
liquidTagOpenPaginateMarkup: {
type: ConcreteNodeTypes.PaginateMarkup,
collection: 0,
pageSize: 4,
args: 6,
locStart,
locEnd,
},

liquidTagClose: {
type: ConcreteNodeTypes.LiquidTagClose,
Expand Down Expand Up @@ -508,7 +533,6 @@ export function toLiquidHtmlCST(text: string): LiquidHtmlCST {
locEnd,
},
renderAliasExpression: 3,
renderArguments: 0,

liquidDrop: {
type: ConcreteNodeTypes.LiquidDrop,
Expand Down Expand Up @@ -550,6 +574,7 @@ export function toLiquidHtmlCST(text: string): LiquidHtmlCST {
},
},
arguments: 0,
tagArguments: 0,
positionalArgument: 0,
namedArgument: {
type: ConcreteNodeTypes.NamedArgument,
Expand Down
2 changes: 2 additions & 0 deletions src/printer/preprocess/augment-with-css-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function getCssDisplay(
case NodeTypes.Range:
case NodeTypes.VariableLookup:
case NodeTypes.AssignMarkup:
case NodeTypes.PaginateMarkup:
case NodeTypes.RenderMarkup:
case NodeTypes.RenderVariableExpression:
return 'should not be relevant';
Expand Down Expand Up @@ -148,6 +149,7 @@ function getNodeCssStyleWhiteSpace(node: AugmentedNode<WithSiblings>): string {
case NodeTypes.Range:
case NodeTypes.VariableLookup:
case NodeTypes.AssignMarkup:
case NodeTypes.PaginateMarkup:
case NodeTypes.RenderMarkup:
case NodeTypes.RenderVariableExpression:
return 'should not be relevant';
Expand Down
4 changes: 4 additions & 0 deletions src/printer/print/liquid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ function printNamedLiquidBlock(
]);
}

case NamedTags.paginate: {
return tag(line);
}

default: {
return assertNever(node);
}
Expand Down
22 changes: 22 additions & 0 deletions src/printer/printer-liquid-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,28 @@ function printNode(
return [node.name, ' = ', path.call(print, 'value')];
}

case NodeTypes.PaginateMarkup: {
const doc = [
path.call(print, 'collection'),
line,
'by ',
path.call(print, 'pageSize'),
];

if (node.args.length > 0) {
doc.push([
',',
line,
join(
[',', line],
path.map((p) => print(p), 'args'),
),
]);
}

return doc;
}

case NodeTypes.RenderMarkup: {
const snippet = path.call(print, 'snippet');
const doc: Doc = [snippet];
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export enum NodeTypes {
VariableLookup = 'VariableLookup',

AssignMarkup = 'AssignMarkup',
PaginateMarkup = 'PaginateMarkup',
RenderMarkup = 'RenderMarkup',
RenderVariableExpression = 'RenderVariableExpression',
}
Expand All @@ -55,6 +56,7 @@ export enum NamedTags {
render = 'render',
include = 'include',
form = 'form',
paginate = 'paginate',
}

export const HtmlNodeTypes = [
Expand Down
26 changes: 26 additions & 0 deletions test/liquid-tag-paginate/fixed.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
It takes a collection and a pageSize
{% paginate collection by pageSize %}{% endpaginate %}

It breaks on by pageSize when printWidth is hit
printWidth: 1
{% paginate collection
by pageSize
%}
{{ collection }}
{% endpaginate %}

It is undocumented, but it also accepts named attributes (window_size). Those are comma separated.
printWidth: 80
{% paginate collection by pageSize, window_size: 50, attr: (0..2) %}
{{ collection }}
{% endpaginate %}

It is undocumented, but it also accepts named attributes (window_size). We print
those comma separated and on a new line when it breaks.
printWidth: 1
{% paginate collection
by pageSize,
window_size: 50
%}
{{ collection }}
{% endpaginate %}
22 changes: 22 additions & 0 deletions test/liquid-tag-paginate/index.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
It takes a collection and a pageSize
{% paginate collection by pageSize %}{% endpaginate %}

It breaks on by pageSize when printWidth is hit
printWidth: 1
{% paginate collection by pageSize %}
{{ collection }}
{% endpaginate %}

It is undocumented, but it also accepts named attributes (window_size). Those are comma separated.
printWidth: 80
{% paginate collection by pageSize window_size:50, attr:(0.. 2) %}
{{ collection }}
{% endpaginate %}

It is undocumented, but it also accepts named attributes (window_size). We print
those comma separated and on a new line when it breaks.
printWidth: 1
{% paginate collection by pageSize window_size: 50 %}
{{ collection }}
{% endpaginate %}

6 changes: 6 additions & 0 deletions test/liquid-tag-paginate/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { assertFormattedEqualsFixed } from '../test-helpers';
import * as path from 'path';

describe(`Unit: ${path.basename(__dirname)}`, () => {
assertFormattedEqualsFixed(__dirname);
});

0 comments on commit 29874ff

Please sign in to comment.