-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathDocumentLinksProvider.ts
107 lines (95 loc) · 3.38 KB
/
DocumentLinksProvider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { LiquidHtmlNode, LiquidString, NamedTags, NodeTypes } from '@shopify/liquid-html-parser';
import { SourceCodeType } from '@shopify/theme-check-common';
import { DocumentLink, Range } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { URI, Utils } from 'vscode-uri';
import { DocumentManager } from '../documents';
import { visit, Visitor } from '@shopify/theme-check-common';
export class DocumentLinksProvider {
constructor(
private documentManager: DocumentManager,
private findThemeRootURI: (uri: string) => Promise<string>,
) {}
async documentLinks(uriString: string): Promise<DocumentLink[]> {
const sourceCode = this.documentManager.get(uriString);
if (
!sourceCode ||
sourceCode.type !== SourceCodeType.LiquidHtml ||
sourceCode.ast instanceof Error
) {
return [];
}
const rootUri = await this.findThemeRootURI(uriString);
const visitor = documentLinksVisitor(sourceCode.textDocument, URI.parse(rootUri));
return visit(sourceCode.ast, visitor);
}
}
function documentLinksVisitor(
textDocument: TextDocument,
root: URI,
): Visitor<SourceCodeType.LiquidHtml, DocumentLink> {
return {
LiquidTag(node) {
// {% render 'snippet' %}
// {% include 'snippet' %}
if (
(node.name === 'render' || node.name === 'include') &&
typeof node.markup !== 'string' &&
isLiquidString(node.markup.snippet)
) {
const snippet = node.markup.snippet;
return DocumentLink.create(
range(textDocument, snippet),
Utils.resolvePath(root, 'snippets', snippet.value + '.liquid').toString(),
);
}
// {% section 'section' %}
if (
node.name === 'section' &&
typeof node.markup !== 'string' &&
isLiquidString(node.markup)
) {
const sectionName = node.markup;
return DocumentLink.create(
range(textDocument, sectionName),
Utils.resolvePath(root, 'sections', sectionName.value + '.liquid').toString(),
);
}
// {% content_for 'block', type: 'block_name' %}
if (
node.name === NamedTags.content_for &&
typeof node.markup !== 'string'
) {
const typeArg = node.markup.args.find((arg) => arg.name === 'type');
if (typeArg && typeArg.value.type === 'String') {
return DocumentLink.create(
range(textDocument, typeArg.value),
Utils.resolvePath(root, 'blocks', typeArg.value.value + '.liquid').toString(),
);
}
}
},
// {{ 'theme.js' | asset_url }}
LiquidVariable(node) {
if (node.filters.length === 0 || node.filters[0].name !== 'asset_url') {
return;
}
if (!isLiquidString(node.expression)) {
return;
}
const expression = node.expression;
return DocumentLink.create(
range(textDocument, node.expression),
Utils.resolvePath(root, 'assets', expression.value).toString(),
);
},
};
}
function range(textDocument: TextDocument, node: { position: LiquidHtmlNode['position'] }): Range {
const start = textDocument.positionAt(node.position.start + 1);
const end = textDocument.positionAt(node.position.end - 1);
return Range.create(start, end);
}
function isLiquidString(node: LiquidHtmlNode): node is LiquidString {
return node.type === NodeTypes.String;
}