Skip to content

Commit e969f23

Browse files
committed
Support default.settings completion + hover
1 parent 383cc04 commit e969f23

File tree

5 files changed

+257
-152
lines changed

5 files changed

+257
-152
lines changed

.changeset/rotten-needles-allow.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@shopify/theme-language-server-common': minor
3+
---
4+
5+
Support `default.settings` completion + hover
6+
7+
- Fix bug where `presets.[].settings` hover only worked on first setting
8+

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

Lines changed: 125 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('Unit: ReferencedSettingPropertyCompletionProvider', () => {
2323
jsonLanguageService = mockJSONLanguageService(
2424
rootUri,
2525
documentManager,
26-
async (uri: string) => ({
26+
async (_uri: string) => ({
2727
general: {
2828
fake: 'Fake Setting',
2929
},
@@ -46,86 +46,136 @@ describe('Unit: ReferencedSettingPropertyCompletionProvider', () => {
4646
});
4747
});
4848

49-
describe('valid schema', () => {
50-
it('completes preset setting property when settings exist', async () => {
51-
const source = `
52-
{% schema %}
53-
{
54-
"settings": [
55-
{"id": "custom-setting"},
56-
{"id": "fake-setting"},
57-
{},
58-
],
59-
"presets": [{
60-
"settings": {
61-
"█": "",
62-
}
63-
}]
64-
}
65-
{% endschema %}
66-
`;
67-
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
68-
const completions = await jsonLanguageService.completions(params);
49+
describe('section file with valid schema', () => {
50+
const tests = [
51+
{
52+
label: 'preset',
53+
schemaTemplate: (setting: object) => {
54+
return {
55+
presets: [setting],
56+
};
57+
},
58+
},
59+
{
60+
label: 'default',
61+
schemaTemplate: (setting: object) => {
62+
return {
63+
default: setting,
64+
};
65+
},
66+
},
67+
];
6968

70-
assert(isCompletionList(completions));
71-
expect(completions.items).to.have.lengthOf(2);
72-
expect(completions.items.map((item) => item.label)).to.include.members([
73-
`"custom-setting"`,
74-
`"fake-setting"`,
75-
]);
76-
});
69+
for (const test of tests) {
70+
describe(`${test.label} settings`, () => {
71+
it(`completes ${test.label} setting property when settings exist`, async () => {
72+
const schema = {
73+
settings: [{ id: 'custom-setting' }, { id: 'fake-setting' }, {}],
74+
...test.schemaTemplate({
75+
settings: {
76+
'█': '',
77+
},
78+
}),
79+
};
80+
const source = `
81+
{% schema %}
82+
${JSON.stringify(schema)}
83+
{% endschema %}
84+
`;
85+
const params = getRequestParams(documentManager, 'sections/section.liquid', source);
86+
const completions = await jsonLanguageService.completions(params);
7787

78-
it('offers no suggestions for preset setting property when there are no settings', async () => {
79-
const source = `
80-
{% schema %}
81-
{
82-
"presets": [{
83-
"settings": {
84-
"█": "",
85-
}
86-
}]
87-
}
88-
{% endschema %}
89-
`;
90-
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
91-
const completions = await jsonLanguageService.completions(params);
88+
assert(isCompletionList(completions));
89+
expect(completions.items).to.have.lengthOf(2);
90+
expect(completions.items.map((item) => item.label)).to.include.members([
91+
`"custom-setting"`,
92+
`"fake-setting"`,
93+
]);
94+
});
9295

93-
assert(isCompletionList(completions));
94-
expect(completions.items).to.have.lengthOf(0);
95-
expect(completions.items.map((item) => item.label)).to.include.members([]);
96-
});
96+
it(`offers no suggestions for ${test.label} setting property when there are no settings`, async () => {
97+
const schema = {
98+
...test.schemaTemplate({
99+
settings: {
100+
'█': '',
101+
},
102+
}),
103+
};
104+
const source = `
105+
{% schema %}
106+
${JSON.stringify(schema)}
107+
{% endschema %}
108+
`;
109+
const params = getRequestParams(documentManager, 'sections/section.liquid', source);
110+
const completions = await jsonLanguageService.completions(params);
97111

98-
it('offers presets setting completion with docs from setting.label', async () => {
99-
const source = `
100-
{% schema %}
101-
{
102-
"settings": [
103-
{"id": "custom-setting", "label": "Custom Setting"},
104-
{"id": "fake-setting", "label": "t:general.fake"},
105-
],
106-
"presets": [{
107-
"settings": {
108-
"█": "",
112+
assert(isCompletionList(completions));
113+
expect(completions.items).to.have.lengthOf(0);
114+
expect(completions.items.map((item) => item.label)).to.include.members([]);
115+
});
116+
117+
it(`offers ${test.label} setting completion with docs from setting.label`, async () => {
118+
const schema = {
119+
settings: [
120+
{ id: 'custom-setting', label: 'Custom Setting' },
121+
{ id: 'fake-setting', label: 't:general.fake' },
122+
{},
123+
],
124+
...test.schemaTemplate({
125+
settings: {
126+
'█': '',
127+
},
128+
}),
129+
};
130+
const source = `
131+
{% schema %}
132+
${JSON.stringify(schema)}
133+
{% endschema %}
134+
`;
135+
const params = getRequestParams(documentManager, 'sections/section.liquid', source);
136+
const completions = await jsonLanguageService.completions(params);
137+
138+
assert(isCompletionList(completions));
139+
expect(completions.items).to.have.lengthOf(2);
140+
expect(completions.items).toEqual(
141+
expect.arrayContaining([
142+
expect.objectContaining({
143+
documentation: { kind: 'markdown', value: 'Custom Setting' },
144+
}),
145+
expect.objectContaining({
146+
documentation: { kind: 'markdown', value: 'Fake Setting' },
147+
}),
148+
]),
149+
);
150+
});
151+
});
152+
}
153+
154+
describe('block file with valid schema', () => {
155+
it(`offers no suggestions for default setting property when file is block`, async () => {
156+
const source = `
157+
{% schema %}
158+
{
159+
"settings": [
160+
{"id": "custom-setting"},
161+
{"id": "fake-setting"},
162+
{},
163+
],
164+
"default": {
165+
"settings": {
166+
"█": ""
167+
}
109168
}
110-
}]
111-
}
112-
{% endschema %}
113-
`;
114-
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
115-
const completions = await jsonLanguageService.completions(params);
169+
}
170+
{% endschema %}
171+
`;
172+
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
173+
const completions = await jsonLanguageService.completions(params);
116174

117-
assert(isCompletionList(completions));
118-
expect(completions.items).to.have.lengthOf(2);
119-
expect(completions.items).toEqual(
120-
expect.arrayContaining([
121-
expect.objectContaining({
122-
documentation: { kind: 'markdown', value: 'Custom Setting' },
123-
}),
124-
expect.objectContaining({
125-
documentation: { kind: 'markdown', value: 'Fake Setting' },
126-
}),
127-
]),
128-
);
175+
assert(isCompletionList(completions));
176+
expect(completions.items).to.have.lengthOf(0);
177+
expect(completions.items.map((item) => item.label)).to.include.members([]);
178+
});
129179
});
130180
});
131181

packages/theme-language-server-common/src/json/completions/providers/ReferencedSettingPropertyCompletionProvider.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import { isError, SourceCodeType } from '@shopify/theme-check-common';
33
import { JSONPath } from 'vscode-json-languageservice';
44
import { JSONCompletionItem } from 'vscode-json-languageservice/lib/umd/jsonContributions';
55
import { RequestContext } from '../../RequestContext';
6-
import { fileMatch } from '../../utils';
6+
import { fileMatch, isBlockFile, isSectionFile } from '../../utils';
77
import { JSONCompletionProvider } from '../JSONCompletionProvider';
88
import { isSectionOrBlockSchema } from './BlockTypeCompletionProvider';
99
import { CompletionItemKind } from 'vscode-languageserver-protocol';
1010
import { GetTranslationsForURI, renderTranslation, translationValue } from '../../../translations';
1111

1212
/**
13-
* The ReferencedSettingPropertyCompletionProvider offers property completions of the
14-
* `presets.[].settings.[]` objects inside section and theme block `{% schema %}` tags.
13+
* The ReferencedSettingPropertyCompletionProvider offers property completions for:
14+
* - `presets.[].settings.[]` objects inside `{% schema %}` tag in sections and blocks
15+
* - `default.settings` object inside `{% schema %}` tag in sections
1516
*
1617
* @example
1718
* {% schema %}
@@ -22,7 +23,12 @@ import { GetTranslationsForURI, renderTranslation, translationValue } from '../.
2223
* { "█" },
2324
* ]
2425
* },
25-
* ]
26+
* ],
27+
* "default": {
28+
* "settings": {
29+
* "█"
30+
* }
31+
* }
2632
* }
2733
* {% endschema %}
2834
*/
@@ -32,10 +38,16 @@ export class ReferencedSettingPropertyCompletionProvider implements JSONCompleti
3238
constructor(public getDefaultSchemaTranslations: GetTranslationsForURI) {}
3339

3440
async completeProperty(context: RequestContext, path: JSONPath): Promise<JSONCompletionItem[]> {
41+
if (context.doc.type !== SourceCodeType.LiquidHtml) return [];
42+
43+
// section files can have schemas with `presets` and `default`
44+
// block files can have schemas with `presets` only
3545
if (
36-
!fileMatch(context.doc.uri, this.uriPatterns) ||
37-
context.doc.type !== SourceCodeType.LiquidHtml ||
38-
!isPresetSettingsPath(path)
46+
!(
47+
isSectionFile(context.doc.uri) &&
48+
(isPresetSettingsPath(path) || isDefaultSettingsPath(path))
49+
) &&
50+
!(isBlockFile(context.doc.uri) && isPresetSettingsPath(path))
3951
) {
4052
return [];
4153
}
@@ -100,5 +112,9 @@ export class ReferencedSettingPropertyCompletionProvider implements JSONCompleti
100112
}
101113

102114
function isPresetSettingsPath(path: JSONPath) {
103-
return path.at(0) === 'presets' && path.at(2) === 'settings';
115+
return path.length === 3 && path.at(0) === 'presets' && path.at(2) === 'settings';
116+
}
117+
118+
function isDefaultSettingsPath(path: JSONPath) {
119+
return path.length === 2 && path.at(0) === 'default' && path.at(1) === 'settings';
104120
}

0 commit comments

Comments
 (0)