Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-dot-lookup-digit-property.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/liquid-html-parser': minor
---

Allow property names starting with a digit in dot lookups (e.g. `{{ address.2country.name }}`). This fixes an autocomplete regression in the language server where suggestions were silently dropped after a property whose name started with a number, because the grammar treated the dot lookup as an unparseable token. `variableSegment` (used for variable declarations and filter names) is unchanged and still rejects leading digits.
10 changes: 9 additions & 1 deletion packages/liquid-html-parser/grammar/liquid-html.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,12 @@ Liquid <: Helpers {
| indexLookup<delim>
| dotLookup
indexLookup<delim> = space* "[" space* liquidExpression<delim> space* "]"
dotLookup = space* "." space* identifier
dotLookup = space* "." space* propertyName
// Property names accessed via dot lookup can start with a digit
// (e.g. `settings.2country`). This differs from `variableSegment`, which
// is used for variable declarations and filter names where leading
// digits are not allowed.
propertyName = (letter | "_" | digit) (~endOfTagName identifierCharacter)* "?"?

liquidFilter<delim> = space* "|" space* identifier (space* ":" space* arguments<delim> (space* ",")?)?

Expand Down Expand Up @@ -583,6 +588,7 @@ WithPlaceholderLiquid <: Liquid {
snippetExpression renderVariableExpression? renderAliasExpression? completionModeRenderArguments
liquidTagName := (letter | "█") (alnum | "_")*
variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
propertyName := (letter | "_" | digit | "█") (identifierCharacter | "█")* "?"?
liquidDoc :=
liquidDocStart
liquidDocBody
Expand All @@ -597,6 +603,7 @@ WithPlaceholderLiquidStatement <: LiquidStatement {
snippetExpression renderVariableExpression? renderAliasExpression? completionModeRenderArguments
liquidTagName := (letter | "█") (alnum | "_")*
variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
propertyName := (letter | "_" | digit | "█") (identifierCharacter | "█")* "?"?
liquidDoc :=
liquidDocStart
liquidDocBody
Expand All @@ -611,6 +618,7 @@ WithPlaceholderLiquidHTML <: LiquidHTML {
snippetExpression renderVariableExpression? renderAliasExpression? completionModeRenderArguments
liquidTagName := (letter | "█") (alnum | "_")*
variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
propertyName := (letter | "_" | digit | "█") (identifierCharacter | "█")* "?"?
leadingTagNameTextNode := (letter | "█") (alnum | "-" | ":" | "█")*
trailingTagNameTextNode := (alnum | "-" | ":" | "█")+
liquidDoc :=
Expand Down
14 changes: 14 additions & 0 deletions packages/liquid-html-parser/src/stage-1-cst.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ describe('Unit: Stage 1 (CST)', () => {
{ expression: `x["y"].z`, name: 'x', lookups: ['y', 'z'] },
{ expression: `["product"]`, name: null, lookups: ['product'] },
{ expression: `page.about-us`, name: 'page', lookups: ['about-us'] },
// Property names (dot lookup targets) can start with a digit
{ expression: `address.2country`, name: 'address', lookups: ['2country'] },
{ expression: `address.2country.name`, name: 'address', lookups: ['2country', 'name'] },
{ expression: `["x"].y`, name: null, lookups: ['x', 'y'] },
{ expression: `["x"]["y"]`, name: null, lookups: ['x', 'y'] },
{ expression: `x[y]`, name: 'x', lookups: [v('y')] },
Expand Down Expand Up @@ -1882,6 +1885,17 @@ describe('Unit: Stage 1 (CST)', () => {
expectPath(cst, '0.markup.expression.lookups.0.type').to.eql('VariableLookup');
expectPath(cst, '0.markup.expression.lookups.0.name').to.eql('█');

// Completion after a property whose name starts with a digit
cst = toCST('{{ address.2country.█ }}');
expectPath(cst, '0.type').to.eql('LiquidVariableOutput');
expectPath(cst, '0.markup.type').to.eql('LiquidVariable');
expectPath(cst, '0.markup.expression.type').to.eql('VariableLookup');
expectPath(cst, '0.markup.expression.name').to.eql('address');
expectPath(cst, '0.markup.expression.lookups.0.type').to.eql('String');
expectPath(cst, '0.markup.expression.lookups.0.value').to.eql('2country');
expectPath(cst, '0.markup.expression.lookups.1.type').to.eql('String');
expectPath(cst, '0.markup.expression.lookups.1.value').to.eql('█');

cst = toCST('{% echo █ %}');
expectPath(cst, '0.type').to.eql('LiquidTag');
expectPath(cst, '0.markup.type').to.eql('LiquidVariable');
Expand Down
Loading