Skip to content

Commit

Permalink
Merge branch 'main' into fd-remove-discussions-link
Browse files Browse the repository at this point in the history
  • Loading branch information
frandiox authored Feb 12, 2025
2 parents b0c77b6 + 5c54d85 commit 5a5e2c9
Show file tree
Hide file tree
Showing 43 changed files with 1,279 additions and 127 deletions.
9 changes: 9 additions & 0 deletions .changeset/forty-moose-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@shopify/theme-check-common': minor
'@shopify/theme-check-node': minor
---

Theme check verifies if setting key exists within block schemas and section schemas

- Check if the keys inside `presets.[].settings` and `default.settings` exist as `settings.[].id` in the same file
- Check if the keys inside `presets.[](recursive .blocks.[]).settings` and `default.blocks.[].settings` exist as `settings.[].id` inside the referenced block's file
5 changes: 5 additions & 0 deletions .changeset/four-snakes-compare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/liquid-html-parser': minor
---

[LiquidDoc]: Add parser support for @description annotations. These can be placed anywhere within the header, and can span numerous lines.
7 changes: 7 additions & 0 deletions .changeset/fresh-suns-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@shopify/liquid-html-parser': minor
'@shopify/theme-language-server-common': minor
---

- Support parsing incomplete content_for tags in completion context
- Support content_for param completion
5 changes: 5 additions & 0 deletions .changeset/orange-emus-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/theme-language-server-common': patch
---

Update document manager on git operations
6 changes: 6 additions & 0 deletions .changeset/popular-readers-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@shopify/theme-language-server-common': minor
'@shopify/theme-check-common': minor
---

Move `getSnippetDefinition` to theme-check-common
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<div align="center">

🗣 [Slack](https://join.slack.com/t/shopifypartners/shared_invite/zt-sdr2quab-mGkzkttZ2hnVm0~8noSyvw) | 📝 [Changelog](./CHANGELOG.md)
🗣 [Slack](https://join.slack.com/t/shopifypartners/shared_invite/zt-sdr2quab-mGkzkttZ2hnVm0~8noSyvw) | 📝 [Changelog](https://github.com/Shopify/theme-tools/blob/main/packages/vscode-extension/CHANGELOG.md)

</div>

Expand Down
14 changes: 13 additions & 1 deletion packages/liquid-html-parser/grammar/liquid-html.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ Liquid <: Helpers {
contentForType (argumentSeparatorOptionalComma contentForTagArgument) (space* ",")? space*

contentForTagArgument = listOf<contentForNamedArgument<delimTag>, argumentSeparatorOptionalComma>
completionModeContentForTagArgument = listOf<contentForNamedArgument<delimTag>, argumentSeparatorOptionalComma> (argumentSeparator? (liquidVariableLookup<delimTag>))?
contentForNamedArgument<delim> = (variableSegment ("." variableSegment)*) space* ":" space* (liquidExpression<delim>)

contentForType = liquidString<delimTag>
Expand Down Expand Up @@ -393,13 +394,18 @@ LiquidDoc <: Helpers {
LiquidDocNode =
| paramNode
| exampleNode
| descriptionNode
| fallbackNode

// By default, space matches new lines as well. We override it here to make writing rules easier.
strictSpace = " " | "\t"
// We use this as an escape hatch to stop matching TextNode and try again when one of these characters is encountered
openControl:= "@" | end

descriptionNode = "@description" strictSpace* descriptionContent
descriptionContent = anyExceptStar<endOfDescription>
endOfDescription = strictSpace* openControl

paramNode = "@param" strictSpace* paramType? strictSpace* (optionalParamName | paramName) (strictSpace* "-")? strictSpace* paramDescription
paramType = "{" strictSpace* paramTypeContent strictSpace* "}"
paramTypeContent = anyExceptStar<("}"| strictSpace)>
Expand Down Expand Up @@ -546,18 +552,24 @@ StrictLiquidHTML <: LiquidHTML {

WithPlaceholderLiquid <: Liquid {
liquidFilter<delim> := space* "|" space* identifier (space* ":" space* filterArguments<delim> (space* ",")?)?
liquidTagContentForMarkup :=
contentForType (argumentSeparatorOptionalComma completionModeContentForTagArgument) (space* ",")? space*
liquidTagName := (letter | "█") (alnum | "_")*
variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
}

WithPlaceholderLiquidStatement <: LiquidStatement {
liquidFilter<delim> := space* "|" space* identifier (space* ":" space* filterArguments<delim> (space* ",")?)?
liquidTagContentForMarkup :=
contentForType (argumentSeparatorOptionalComma completionModeContentForTagArgument) (space* ",")? space*
liquidTagName := (letter | "█") (alnum | "_")*
variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
}

WithPlaceholderLiquidHTML <: LiquidHTML {
liquidFilter<delim> := space* "|" space* identifier (space* ":" space* filterArguments<delim> (space* ",")?)?
liquidTagContentForMarkup :=
contentForType (argumentSeparatorOptionalComma completionModeContentForTagArgument) (space* ",")? space*
liquidTagName := (letter | "█") (alnum | "_")*
variableSegment := (letter | "_" | "█") (identifierCharacter | "█")*
leadingTagNameTextNode := (letter | "█") (alnum | "-" | ":" | "█")*
Expand Down
94 changes: 83 additions & 11 deletions packages/liquid-html-parser/src/stage-1-cst.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1237,7 +1237,7 @@ describe('Unit: Stage 1 (CST)', () => {
expectPath(cst, '0.type').to.equal('LiquidRawTag');
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.children.0.type').to.equal('LiquidDocExampleNode');
expectPath(cst, '0.children.0.exampleContent.value').to.equal('');
expectPath(cst, '0.children.0.content.value').to.equal('');
});

it('should parse example tag with content that has leading whitespace', () => {
Expand All @@ -1247,11 +1247,9 @@ describe('Unit: Stage 1 (CST)', () => {
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.children.0.type').to.equal('LiquidDocExampleNode');
expectPath(cst, '0.children.0.name').to.equal('example');
expectPath(cst, '0.children.0.exampleContent.value').to.equal('hello there');
expectPath(cst, '0.children.0.exampleContent.locStart').to.equal(
testStr.indexOf('hello there'),
);
expectPath(cst, '0.children.0.exampleContent.locEnd').to.equal(
expectPath(cst, '0.children.0.content.value').to.equal('hello there');
expectPath(cst, '0.children.0.content.locStart').to.equal(testStr.indexOf('hello there'));
expectPath(cst, '0.children.0.content.locEnd').to.equal(
testStr.indexOf('hello there') + 'hello there'.length,
);
});
Expand All @@ -1268,7 +1266,7 @@ describe('Unit: Stage 1 (CST)', () => {
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.children.0.type').to.equal('LiquidDocExampleNode');
expectPath(cst, '0.children.0.name').to.equal('example');
expectPath(cst, '0.children.0.exampleContent.value').to.equal(
expectPath(cst, '0.children.0.content.value').to.equal(
'\n This is an example\n It supports multiple lines\n',
);
});
Expand All @@ -1282,7 +1280,7 @@ describe('Unit: Stage 1 (CST)', () => {
cst = toCST(testStr);
expectPath(cst, '0.children.0.type').to.equal('LiquidDocExampleNode');
expectPath(cst, '0.children.0.name').to.equal('example');
expectPath(cst, '0.children.0.exampleContent.value').to.equal(
expectPath(cst, '0.children.0.content.value').to.equal(
'\n This is an example\n',
);
expectPath(cst, '0.children.1.type').to.equal('LiquidDocParamNode');
Expand All @@ -1300,7 +1298,7 @@ describe('Unit: Stage 1 (CST)', () => {
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.children.0.type').to.equal('LiquidDocExampleNode');
expectPath(cst, '0.children.0.name').to.equal('example');
expectPath(cst, '0.children.0.exampleContent.value').to.equal(
expectPath(cst, '0.children.0.content.value').to.equal(
'hello there my friend\n This is an example\n It supports multiple lines\n',
);
});
Expand All @@ -1312,9 +1310,73 @@ describe('Unit: Stage 1 (CST)', () => {
{% enddoc %}`;
cst = toCST(testStr);
expectPath(cst, '0.children.0.type').to.equal('LiquidDocExampleNode');
expectPath(cst, '0.children.0.exampleContent.value').to.equal('hello there\n');
expectPath(cst, '0.children.0.content.value').to.equal('hello there\n');
expectPath(cst, '0.children.1.type').to.equal('LiquidDocExampleNode');
expectPath(cst, '0.children.1.exampleContent.value').to.equal('second example\n');
expectPath(cst, '0.children.1.content.value').to.equal('second example\n');
});

it('should parse @description node', () => {
const testStr = `{% doc %} @description {%- enddoc %}`;
cst = toCST(testStr);
expectPath(cst, '0.type').to.equal('LiquidRawTag');
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.children.0.type').to.equal('LiquidDocDescriptionNode');
expectPath(cst, '0.children.0.content.value').to.equal('');
});

it('should parse @description node', () => {
const testStr = `{% doc %}
@description This is a description
@description This is a second description
{% enddoc %}`;
cst = toCST(testStr);
expectPath(cst, '0.children.0.type').to.equal('LiquidDocDescriptionNode');
expectPath(cst, '0.children.0.content.value').to.equal('This is a description\n');

expectPath(cst, '0.children.1.type').to.equal('LiquidDocDescriptionNode');
expectPath(cst, '0.children.1.content.value').to.equal('This is a second description\n');
});

it('should parse and strip whitespace from description tag with content that has leading whitespace', () => {
const testStr = `{% doc %} @description hello there {%- enddoc %}`;
cst = toCST(testStr);
expectPath(cst, '0.type').to.equal('LiquidRawTag');
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.children.0.type').to.equal('LiquidDocDescriptionNode');
expectPath(cst, '0.children.0.name').to.equal('description');
expectPath(cst, '0.children.0.content.value').to.equal('hello there');
expectPath(cst, '0.children.0.content.locStart').to.equal(testStr.indexOf('hello there'));
expectPath(cst, '0.children.0.content.locEnd').to.equal(
testStr.indexOf('hello there') + 'hello there'.length,
);
});

it('should parse description node with whitespace and new lines', () => {
const testStr = `{% doc %}
@description hello there my friend
This is a description
It supports multiple lines
{% enddoc %}`;
cst = toCST(testStr);
expectPath(cst, '0.type').to.equal('LiquidRawTag');
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.children.0.type').to.equal('LiquidDocDescriptionNode');
expectPath(cst, '0.children.0.name').to.equal('description');
expectPath(cst, '0.children.0.content.value').to.equal(
'hello there my friend\n This is a description\n It supports multiple lines\n',
);
});

it('should parse multiple description nodes', () => {
const testStr = `{% doc %}
@description hello there
@description second description
{% enddoc %}`;
cst = toCST(testStr);
expectPath(cst, '0.children.0.type').to.equal('LiquidDocDescriptionNode');
expectPath(cst, '0.children.0.content.value').to.equal('hello there\n');
expectPath(cst, '0.children.1.type').to.equal('LiquidDocDescriptionNode');
expectPath(cst, '0.children.1.content.value').to.equal('second description\n');
});
}
});
Expand Down Expand Up @@ -1714,6 +1776,16 @@ describe('Unit: Stage 1 (CST)', () => {
expectPath(cst, '0.markup.filters.0.args.1.type').to.equal('NamedArgument');
expectPath(cst, '0.markup.filters.0.args.2.type').to.equal('VariableLookup');
});

it('should parse incomplete parameters for content_for tags', () => {
const toCST = (source: string) => toLiquidHtmlCST(source, { mode: 'completion' });

cst = toCST(`{% content_for "blocks", id: 1, cl█ %}`);

expectPath(cst, '0.markup.type').to.equal('ContentForMarkup');
expectPath(cst, '0.markup.args.0.type').to.equal('NamedArgument');
expectPath(cst, '0.markup.args.1.type').to.equal('VariableLookup');
});
});

function makeExpectPath(message: string) {
Expand Down
32 changes: 29 additions & 3 deletions packages/liquid-html-parser/src/stage-1-cst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export enum ConcreteNodeTypes {

LiquidDocParamNode = 'LiquidDocParamNode',
LiquidDocParamNameNode = 'LiquidDocParamNameNode',
LiquidDocDescriptionNode = 'LiquidDocDescriptionNode',
LiquidDocExampleNode = 'LiquidDocExampleNode',
}

Expand Down Expand Up @@ -124,10 +125,16 @@ export interface ConcreteLiquidDocParamNameNode
required: boolean;
}

export interface ConcreteLiquidDocDescriptionNode
extends ConcreteBasicNode<ConcreteNodeTypes.LiquidDocDescriptionNode> {
name: 'description';
content: ConcreteTextNode;
}

export interface ConcreteLiquidDocExampleNode
extends ConcreteBasicNode<ConcreteNodeTypes.LiquidDocExampleNode> {
name: 'example';
exampleContent: ConcreteTextNode;
content: ConcreteTextNode;
}

export interface ConcreteHtmlNodeBase<T> extends ConcreteBasicNode<T> {
Expand Down Expand Up @@ -469,7 +476,10 @@ export type LiquidHtmlCST = LiquidHtmlConcreteNode[];

export type LiquidCST = LiquidConcreteNode[];

export type LiquidDocConcreteNode = ConcreteLiquidDocParamNode | ConcreteLiquidDocExampleNode;
export type LiquidDocConcreteNode =
| ConcreteLiquidDocParamNode
| ConcreteLiquidDocExampleNode
| ConcreteLiquidDocDescriptionNode;

interface Mapping {
[k: string]: number | TemplateMapping | TopLevelFunctionMapping;
Expand Down Expand Up @@ -927,6 +937,13 @@ function toCST<T>(
simpleArgument: 0,
tagArguments: 0,
contentForTagArgument: 0,
completionModeContentForTagArgument: function (namedArguments, _separator, variableLookup) {
const self = this as any;

return namedArguments
.toAST(self.args.mapping)
.concat(variableLookup.sourceString === '' ? [] : variableLookup.toAST(self.args.mapping));
},
positionalArgument: 0,
namedArgument: {
type: ConcreteNodeTypes.NamedArgument,
Expand Down Expand Up @@ -1357,6 +1374,15 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number)
paramName: 4,
paramDescription: 8,
},
descriptionNode: {
type: ConcreteNodeTypes.LiquidDocDescriptionNode,
name: 'description',
locStart,
locEnd,
source,
content: 2,
},
descriptionContent: textNode,
paramType: 2,
paramTypeContent: textNode,
paramName: {
Expand All @@ -1382,7 +1408,7 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number)
locStart,
locEnd,
source,
exampleContent: 2,
content: 2,
},
exampleContent: textNode,
textValue: textNode,
Expand Down
Loading

0 comments on commit 5a5e2c9

Please sign in to comment.