Skip to content

Commit 3886acb

Browse files
committed
Added support for render tag completion for inline snippets
1 parent b4049b2 commit 3886acb

File tree

3 files changed

+65
-15
lines changed

3 files changed

+65
-15
lines changed

.changeset/flat-geckos-breathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/theme-language-server-common': minor
3+
---
4+
5+
Added completion support for inline snippets in the `render` tag

packages/theme-language-server-common/src/completions/providers/RenderSnippetCompletionProvider.spec.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,23 @@ describe('Module: RenderSnippetCompletionProvider', async () => {
2424

2525
it('should complete snippets correctly', async () => {
2626
await expect(provider).to.complete('{% render "', ['product-card', 'image']);
27-
await expect(provider).to.complete('{% render "product', [
27+
// VSCode handles filtering
28+
});
29+
30+
it('should complete inline snippets', async () => {
31+
const template = `{% snippet my-inline-snippet %}
32+
{% echo 'hello' %}
33+
{% endsnippet %}
34+
35+
{% render m`;
36+
37+
await expect(provider).to.complete(template, ['my-inline-snippet']);
38+
// VSCode handles filtering
39+
await expect(provider).to.complete(template, [
2840
expect.objectContaining({
2941
documentation: {
3042
kind: 'markdown',
31-
value: 'snippets/product-card.liquid',
43+
value: `Inline snippet "my-inline-snippet"`,
3244
},
3345
}),
3446
]);
Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { NodeTypes } from '@shopify/liquid-html-parser';
1+
import { LiquidHtmlNode, LiquidTag, NodeTypes } from '@shopify/liquid-html-parser';
22
import { CompletionItem, CompletionItemKind } from 'vscode-languageserver';
33
import { LiquidCompletionParams } from '../params';
44
import { Provider } from './common';
5+
import { SourceCodeType, visit, isError } from '@shopify/theme-check-common';
6+
import { findInlineSnippetAncestor } from '@shopify/theme-check-common/dist/checks/utils';
57

68
export type GetSnippetNamesForURI = (uri: string) => Promise<string[]>;
79

@@ -14,21 +16,13 @@ export class RenderSnippetCompletionProvider implements Provider {
1416
const { node, ancestors } = params.completionContext;
1517
const parentNode = ancestors.at(-1);
1618

17-
if (
18-
!node ||
19-
!parentNode ||
20-
node.type !== NodeTypes.String ||
21-
parentNode.type !== NodeTypes.RenderMarkup
22-
) {
19+
if (!node || !parentNode || parentNode.type !== NodeTypes.RenderMarkup) {
2320
return [];
2421
}
2522

26-
const options = await this.getSnippetNamesForURI(params.textDocument.uri);
27-
const partial = node.value;
28-
29-
return options
30-
.filter((option) => option.startsWith(partial))
31-
.map(
23+
if (node.type === NodeTypes.String) {
24+
const fileSnippets = await this.getSnippetNamesForURI(params.textDocument.uri);
25+
const fileCompletionItems = fileSnippets.map(
3226
(option: string): CompletionItem => ({
3327
label: option,
3428
kind: CompletionItemKind.Snippet,
@@ -38,5 +32,44 @@ export class RenderSnippetCompletionProvider implements Provider {
3832
},
3933
}),
4034
);
35+
return fileCompletionItems;
36+
} else if (node.type === NodeTypes.VariableLookup) {
37+
const containingSnippet = findInlineSnippetAncestor(ancestors);
38+
const containingSnippetName =
39+
containingSnippet && typeof containingSnippet.markup !== 'string'
40+
? containingSnippet.markup.name
41+
: null;
42+
43+
const fullAst = params.document.ast;
44+
const allInlineSnippets = isError(fullAst) ? [] : getInlineSnippetsNames(fullAst);
45+
46+
const inlineSnippets = allInlineSnippets.filter(
47+
(name) => name !== containingSnippetName,
48+
);
49+
50+
const inlineCompletionItems = inlineSnippets.map(
51+
(option: string): CompletionItem => ({
52+
label: option,
53+
kind: CompletionItemKind.Snippet,
54+
documentation: {
55+
kind: 'markdown',
56+
value: `Inline snippet "${option}"`,
57+
},
58+
}),
59+
);
60+
return inlineCompletionItems;
61+
} else {
62+
return [];
63+
}
4164
}
4265
}
66+
67+
function getInlineSnippetsNames(ast: LiquidHtmlNode): string[] {
68+
return visit<SourceCodeType.LiquidHtml, string>(ast, {
69+
LiquidTag(node: LiquidTag) {
70+
if (node.name === 'snippet' && typeof node.markup !== 'string' && node.markup.name) {
71+
return node.markup.name;
72+
}
73+
},
74+
});
75+
}

0 commit comments

Comments
 (0)