diff --git a/packages/liquid-html-parser/grammar/liquid-html.ohm b/packages/liquid-html-parser/grammar/liquid-html.ohm
index 5d6481c26..7cb62f317 100644
--- a/packages/liquid-html-parser/grammar/liquid-html.ohm
+++ b/packages/liquid-html-parser/grammar/liquid-html.ohm
@@ -389,7 +389,14 @@ LiquidStatement <: Liquid {
}
LiquidDoc <: Helpers {
- Node := (TextNode)*
+ Node := (LiquidDocNode | TextNode)*
+ LiquidDocNode =
+ | paramNode
+ | fallbackNode
+
+ fallbackNode = "@" anyExceptStar
+ paramNode = "@param" space* paramNodeName
+ paramNodeName = anyExceptStar
}
LiquidHTML <: Liquid {
diff --git a/packages/liquid-html-parser/src/stage-1-cst.spec.ts b/packages/liquid-html-parser/src/stage-1-cst.spec.ts
index 7f02c68a8..b5f21991a 100644
--- a/packages/liquid-html-parser/src/stage-1-cst.spec.ts
+++ b/packages/liquid-html-parser/src/stage-1-cst.spec.ts
@@ -984,12 +984,16 @@ describe('Unit: Stage 1 (CST)', () => {
it('should parse doc tags', () => {
for (const { toCST, expectPath } of testCases) {
- const testStr = `{% doc -%} Renders loading-spinner. {%- enddoc %}`;
+ const testStr = `{% doc -%}
+ @param asdf
+ @unsupported
+ {%- enddoc %}`;
cst = toCST(testStr);
+
expectPath(cst, '0.type').to.equal('LiquidRawTag');
expectPath(cst, '0.name').to.equal('doc');
- expectPath(cst, '0.body').to.include('Renders loading-spinner');
+ expectPath(cst, '0.body').to.include('@param asdf');
expectPath(cst, '0.whitespaceStart').to.equal('');
expectPath(cst, '0.whitespaceEnd').to.equal('-');
expectPath(cst, '0.delimiterWhitespaceStart').to.equal('-');
@@ -998,15 +1002,16 @@ describe('Unit: Stage 1 (CST)', () => {
expectPath(cst, '0.blockStartLocEnd').to.equal(0 + '{% doc -%}'.length);
expectPath(cst, '0.blockEndLocStart').to.equal(testStr.length - '{%- enddoc %}'.length);
expectPath(cst, '0.blockEndLocEnd').to.equal(testStr.length);
- expectPath(cst, '0.children').to.deep.equal([
- {
- locEnd: 35,
- locStart: 11,
- source: '{% doc -%} Renders loading-spinner. {%- enddoc %}',
- type: 'TextNode',
- value: 'Renders loading-spinner.',
- },
- ]);
+
+ expectPath(cst, '0.children.0.type').to.equal('LiquidDocParamNode');
+ expectPath(cst, '0.children.0.locStart').to.equal(testStr.indexOf('@param'));
+ expectPath(cst, '0.children.0.locEnd').to.equal(testStr.indexOf('asdf') + 'asdf'.length);
+
+ expectPath(cst, '0.children.1.type').to.equal('TextNode');
+ expectPath(cst, '0.children.1.locStart').to.equal(testStr.indexOf('@unsupported'));
+ expectPath(cst, '0.children.1.locEnd').to.equal(
+ testStr.indexOf('@unsupported') + '@unsupported'.length,
+ );
}
});
diff --git a/packages/liquid-html-parser/src/stage-1-cst.ts b/packages/liquid-html-parser/src/stage-1-cst.ts
index 4fdeffb8c..40b0d12cb 100644
--- a/packages/liquid-html-parser/src/stage-1-cst.ts
+++ b/packages/liquid-html-parser/src/stage-1-cst.ts
@@ -83,6 +83,8 @@ export enum ConcreteNodeTypes {
PaginateMarkup = 'PaginateMarkup',
RenderVariableExpression = 'RenderVariableExpression',
ContentForNamedArgument = 'ContentForNamedArgument',
+
+ LiquidDocParamNode = 'LiquidDocParamNode',
}
export const LiquidLiteralValues = {
@@ -105,6 +107,12 @@ export interface ConcreteBasicNode {
locEnd: number;
}
+export interface ConcreteLiquidDocParamNode
+ extends ConcreteBasicNode {
+ name: string;
+ value: string;
+}
+
export interface ConcreteHtmlNodeBase extends ConcreteBasicNode {
attrList?: ConcreteAttributeNode[];
}
@@ -440,10 +448,13 @@ export type LiquidConcreteNode =
| ConcreteTextNode
| ConcreteYamlFrontmatterNode;
-export type LiquidHtmlCST = LiquidHtmlConcreteNode[];
+export type LiquidHtmlCST = LiquidHtmlConcreteNode[] | LiquidDocCST;
export type LiquidCST = LiquidConcreteNode[];
+type LiquidDocCST = LiquidDocConcreteNode[];
+export type LiquidDocConcreteNode = ConcreteLiquidDocParamNode;
+
interface Mapping {
[k: string]: number | TemplateMapping | TopLevelFunctionMapping;
}
@@ -1306,7 +1317,24 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number)
const LiquidDocMappings: Mapping = {
Node: 0,
- TextNode: {
+ textNode: {
+ type: ConcreteNodeTypes.TextNode,
+ value: function () {
+ return (this as any).sourceString;
+ },
+ locStart,
+ locEnd,
+ source,
+ },
+ paramNode: {
+ type: ConcreteNodeTypes.LiquidDocParamNode,
+ name: 0,
+ value: 2,
+ locStart,
+ locEnd,
+ source,
+ },
+ fallbackNode: {
type: ConcreteNodeTypes.TextNode,
value: function () {
return (this as any).sourceString;
diff --git a/packages/liquid-html-parser/src/stage-2-ast.spec.ts b/packages/liquid-html-parser/src/stage-2-ast.spec.ts
index 84848ccb2..ed1c2467f 100644
--- a/packages/liquid-html-parser/src/stage-2-ast.spec.ts
+++ b/packages/liquid-html-parser/src/stage-2-ast.spec.ts
@@ -1229,22 +1229,21 @@ describe('Unit: Stage 2 (AST)', () => {
expectPath(ast, 'children.0.body.type').toEqual('RawMarkup');
expectPath(ast, 'children.0.body.nodes').toEqual([]);
- ast = toLiquidAST(`{% doc -%} single line doc {%- enddoc %}`);
+ ast = toLiquidAST(`
+ {% doc -%}
+ @param asdf
+ @unsupported this node falls back to a text node
+ {%- enddoc %}
+ `);
expectPath(ast, 'children.0.type').to.eql('LiquidRawTag');
expectPath(ast, 'children.0.name').to.eql('doc');
- expectPath(ast, 'children.0.body.value').to.eql(' single line doc ');
- expectPath(ast, 'children.0.body.nodes.0.type').toEqual('TextNode');
+ expectPath(ast, 'children.0.body.nodes.0.type').to.eql('LiquidDocParamNode');
+ expectPath(ast, 'children.0.body.nodes.0.name').to.eql('@param');
- ast = toLiquidAST(`{% doc -%}
- multi line doc
- multi line doc
- {%- enddoc %}`);
- expectPath(ast, 'children.0.type').to.eql('LiquidRawTag');
- expectPath(ast, 'children.0.name').to.eql('doc');
- expectPath(ast, 'children.0.body.nodes.0.value').to.eql(
- `multi line doc\n multi line doc`,
+ expectPath(ast, 'children.0.body.nodes.1.type').to.eql('TextNode');
+ expectPath(ast, 'children.0.body.nodes.1.value').to.eql(
+ '@unsupported this node falls back to a text node',
);
- expectPath(ast, 'children.0.body.nodes.0.type').toEqual('TextNode');
});
it('should parse unclosed tables with assignments', () => {
diff --git a/packages/liquid-html-parser/src/stage-2-ast.ts b/packages/liquid-html-parser/src/stage-2-ast.ts
index 15c7e61cb..a89defbea 100644
--- a/packages/liquid-html-parser/src/stage-2-ast.ts
+++ b/packages/liquid-html-parser/src/stage-2-ast.ts
@@ -107,7 +107,8 @@ export type LiquidHtmlNode =
| RenderVariableExpression
| LiquidLogicalExpression
| LiquidComparison
- | TextNode;
+ | TextNode
+ | LiquidDocParamNode;
/** The root node of all LiquidHTML ASTs. */
export interface DocumentNode extends ASTNode {
@@ -754,6 +755,11 @@ export interface TextNode extends ASTNode {
value: string;
}
+export interface LiquidDocParamNode extends ASTNode {
+ name: string;
+ value: string;
+}
+
export interface ASTNode {
/**
* The type of the node, as a string.
@@ -1268,6 +1274,17 @@ function buildAst(
break;
}
+ case ConcreteNodeTypes.LiquidDocParamNode: {
+ builder.push({
+ type: NodeTypes.LiquidDocParamNode,
+ name: node.name,
+ position: position(node),
+ source: node.source,
+ value: node.value,
+ });
+ break;
+ }
+
default: {
assertNever(node);
}
diff --git a/packages/liquid-html-parser/src/types.ts b/packages/liquid-html-parser/src/types.ts
index 29648aebb..49efa1bf3 100644
--- a/packages/liquid-html-parser/src/types.ts
+++ b/packages/liquid-html-parser/src/types.ts
@@ -44,6 +44,7 @@ export enum NodeTypes {
RawMarkup = 'RawMarkup',
RenderMarkup = 'RenderMarkup',
RenderVariableExpression = 'RenderVariableExpression',
+ LiquidDocParamNode = 'LiquidDocParamNode',
}
// These are officially supported with special node types
diff --git a/packages/prettier-plugin-liquid/src/printer/preprocess/augment-with-css-properties.ts b/packages/prettier-plugin-liquid/src/printer/preprocess/augment-with-css-properties.ts
index cc16285e5..4ad565c6d 100644
--- a/packages/prettier-plugin-liquid/src/printer/preprocess/augment-with-css-properties.ts
+++ b/packages/prettier-plugin-liquid/src/printer/preprocess/augment-with-css-properties.ts
@@ -128,6 +128,7 @@ function getCssDisplay(node: AugmentedNode, options: LiquidParserO
case NodeTypes.RenderVariableExpression:
case NodeTypes.LogicalExpression:
case NodeTypes.Comparison:
+ case NodeTypes.LiquidDocParamNode:
return 'should not be relevant';
default:
@@ -233,6 +234,7 @@ function getNodeCssStyleWhiteSpace(
case NodeTypes.RenderVariableExpression:
case NodeTypes.LogicalExpression:
case NodeTypes.Comparison:
+ case NodeTypes.LiquidDocParamNode:
return 'should not be relevant';
default:
diff --git a/packages/prettier-plugin-liquid/src/printer/print/liquid.ts b/packages/prettier-plugin-liquid/src/printer/print/liquid.ts
index 778c7262e..ad1ff03bc 100644
--- a/packages/prettier-plugin-liquid/src/printer/print/liquid.ts
+++ b/packages/prettier-plugin-liquid/src/printer/print/liquid.ts
@@ -1,4 +1,4 @@
-import { NodeTypes, NamedTags, isBranchedTag } from '@shopify/liquid-html-parser';
+import { NodeTypes, NamedTags, isBranchedTag, RawMarkup } from '@shopify/liquid-html-parser';
import { Doc, doc } from 'prettier';
import {
@@ -490,6 +490,16 @@ export function printLiquidRawTag(
return [blockStart, ...body, blockEnd];
}
+export function printLiquidDoc(
+ path: AstPath,
+ _options: LiquidParserOptions,
+ print: LiquidPrinter,
+ _args: LiquidPrinterArgs,
+) {
+ const body = path.map((p: any) => print(p), 'nodes');
+ return [indent([hardline, body]), hardline];
+}
+
function innerLeadingWhitespace(node: LiquidTag | LiquidBranch) {
if (!node.firstChild) {
if (node.isDanglingWhitespaceSensitive && node.hasDanglingWhitespace) {
diff --git a/packages/prettier-plugin-liquid/src/printer/printer-liquid-html.ts b/packages/prettier-plugin-liquid/src/printer/printer-liquid-html.ts
index 3905c8008..92797d8c6 100644
--- a/packages/prettier-plugin-liquid/src/printer/printer-liquid-html.ts
+++ b/packages/prettier-plugin-liquid/src/printer/printer-liquid-html.ts
@@ -1,5 +1,6 @@
import {
getConditionalComment,
+ LiquidDocParamNode,
NodeTypes,
Position,
RawMarkupKinds,
@@ -30,6 +31,7 @@ import {
LiquidTag,
LiquidVariableOutput,
nonTraversableProperties,
+ RawMarkup,
TextNode,
} from '../types';
import { assertNever } from '../utils';
@@ -40,6 +42,7 @@ import { printChildren } from './print/children';
import { printElement } from './print/element';
import {
printLiquidBranch,
+ printLiquidDoc,
printLiquidRawTag,
printLiquidTag,
printLiquidVariableOutput,
@@ -210,6 +213,10 @@ function printNode(
}
case NodeTypes.RawMarkup: {
+ if (node.parentNode?.name === 'doc') {
+ return printLiquidDoc(path as AstPath, options, print, args);
+ }
+
const isRawMarkupIdentationSensitive = () => {
switch (node.kind) {
case RawMarkupKinds.typescript:
@@ -547,6 +554,10 @@ function printNode(
return [...doc, ...lookups];
}
+ case NodeTypes.LiquidDocParamNode: {
+ return [node.name, ' ', node.value];
+ }
+
default: {
return assertNever(node);
}
diff --git a/packages/prettier-plugin-liquid/src/test/liquid-doc/fixed.liquid b/packages/prettier-plugin-liquid/src/test/liquid-doc/fixed.liquid
new file mode 100644
index 000000000..c11d85f6f
--- /dev/null
+++ b/packages/prettier-plugin-liquid/src/test/liquid-doc/fixed.liquid
@@ -0,0 +1,9 @@
+It should indent the body
+{% doc %}
+ @param body
+{% enddoc %}
+
+It should not dedent to root
+{% doc %}
+ @param body
+{% enddoc %}
diff --git a/packages/prettier-plugin-liquid/src/test/liquid-doc/index.liquid b/packages/prettier-plugin-liquid/src/test/liquid-doc/index.liquid
new file mode 100644
index 000000000..45638eb76
--- /dev/null
+++ b/packages/prettier-plugin-liquid/src/test/liquid-doc/index.liquid
@@ -0,0 +1,9 @@
+It should indent the body
+{% doc %}
+@param body
+{% enddoc %}
+
+It should not dedent to root
+{% doc %}
+ @param body
+{% enddoc %}
diff --git a/packages/prettier-plugin-liquid/src/test/liquid-doc/index.spec.ts b/packages/prettier-plugin-liquid/src/test/liquid-doc/index.spec.ts
new file mode 100644
index 000000000..bf0e16d16
--- /dev/null
+++ b/packages/prettier-plugin-liquid/src/test/liquid-doc/index.spec.ts
@@ -0,0 +1,7 @@
+import { test } from 'vitest';
+import { assertFormattedEqualsFixed } from '../test-helpers';
+import * as path from 'path';
+
+test('Unit: liquid-doc', async () => {
+ await assertFormattedEqualsFixed(__dirname);
+});
diff --git a/packages/theme-language-server-common/src/completions/params/LiquidCompletionParams.ts b/packages/theme-language-server-common/src/completions/params/LiquidCompletionParams.ts
index 75d6e1e75..9ed9bcaf0 100644
--- a/packages/theme-language-server-common/src/completions/params/LiquidCompletionParams.ts
+++ b/packages/theme-language-server-common/src/completions/params/LiquidCompletionParams.ts
@@ -402,7 +402,8 @@ function findCurrentNode(
case NodeTypes.TextNode:
case NodeTypes.LiquidLiteral:
case NodeTypes.String:
- case NodeTypes.Number: {
+ case NodeTypes.Number:
+ case NodeTypes.LiquidDocParamNode: {
break;
}