1- import { NodeTypes } from '@shopify/liquid-html-parser' ;
1+ import { LiquidHtmlNode , LiquidTag , NodeTypes } from '@shopify/liquid-html-parser' ;
22import { CompletionItem , CompletionItemKind } from 'vscode-languageserver' ;
33import { LiquidCompletionParams } from '../params' ;
44import { Provider } from './common' ;
5+ import { SourceCodeType , visit } from '@shopify/theme-check-common' ;
56
67export type GetSnippetNamesForURI = ( uri : string ) => Promise < string [ ] > ;
78
@@ -14,29 +15,59 @@ export class RenderSnippetCompletionProvider implements Provider {
1415 const { node, ancestors } = params . completionContext ;
1516 const parentNode = ancestors . at ( - 1 ) ;
1617
17- if (
18- ! node ||
19- ! parentNode ||
20- node . type !== NodeTypes . String ||
21- parentNode . type !== NodeTypes . RenderMarkup
22- ) {
18+ if ( ! node || ! parentNode || parentNode . type !== NodeTypes . RenderMarkup ) {
2319 return [ ] ;
2420 }
2521
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 (
32- ( option : string ) : CompletionItem => ( {
33- label : option ,
34- kind : CompletionItemKind . Snippet ,
35- documentation : {
36- kind : 'markdown' ,
37- value : `snippets/${ option } .liquid` ,
38- } ,
39- } ) ,
40- ) ;
22+ let partial = '' ;
23+ if ( node . type === NodeTypes . String ) {
24+ partial = node . value ;
25+ const fileSnippets = await this . getSnippetNamesForURI ( params . textDocument . uri ) ;
26+ const fileCompletionItems = fileSnippets
27+ . filter ( ( option ) => option . startsWith ( partial ) )
28+ . map (
29+ ( option : string ) : CompletionItem => ( {
30+ label : option ,
31+ kind : CompletionItemKind . Snippet ,
32+ documentation : {
33+ kind : 'markdown' ,
34+ value : `snippets/${ option } .liquid` ,
35+ } ,
36+ } ) ,
37+ ) ;
38+ return fileCompletionItems ;
39+ } else if ( node . type === NodeTypes . VariableLookup ) {
40+ partial = node . name || '' ;
41+ const inlineSnippets = getInlineSnippetsNames ( params . completionContext . partialAst ) ;
42+ const inlineCompletionItems = inlineSnippets
43+ . filter ( ( option ) => option . startsWith ( partial ) )
44+ . map (
45+ ( option : string ) : CompletionItem => ( {
46+ label : option ,
47+ kind : CompletionItemKind . Snippet ,
48+ documentation : {
49+ kind : 'markdown' ,
50+ value : `Inline snippet (defined in this file)` ,
51+ } ,
52+ } ) ,
53+ ) ;
54+ return inlineCompletionItems ;
55+ } else {
56+ return [ ] ;
57+ }
4158 }
4259}
60+
61+ function getInlineSnippetsNames ( ast : LiquidHtmlNode ) : string [ ] {
62+ if ( ast instanceof Error ) return [ ] ;
63+
64+ const inlineSnippetNames = visit < SourceCodeType . LiquidHtml , string > ( ast , {
65+ LiquidTag ( node : LiquidTag ) {
66+ if ( node . name === 'snippet' && typeof node . markup !== 'string' && node . markup . name ) {
67+ return node . markup . name ;
68+ }
69+ } ,
70+ } ) ;
71+
72+ return inlineSnippetNames . filter ( ( name ) : name is string => ! ! name ) ;
73+ }
0 commit comments