Skip to content

Commit

Permalink
Support default.settings completion + hover
Browse files Browse the repository at this point in the history
  • Loading branch information
aswamy committed Jan 17, 2025
1 parent 383cc04 commit e969f23
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 152 deletions.
8 changes: 8 additions & 0 deletions .changeset/rotten-needles-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@shopify/theme-language-server-common': minor
---

Support `default.settings` completion + hover

- Fix bug where `presets.[].settings` hover only worked on first setting

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('Unit: ReferencedSettingPropertyCompletionProvider', () => {
jsonLanguageService = mockJSONLanguageService(
rootUri,
documentManager,
async (uri: string) => ({
async (_uri: string) => ({
general: {
fake: 'Fake Setting',
},
Expand All @@ -46,86 +46,136 @@ describe('Unit: ReferencedSettingPropertyCompletionProvider', () => {
});
});

describe('valid schema', () => {
it('completes preset setting property when settings exist', async () => {
const source = `
{% schema %}
{
"settings": [
{"id": "custom-setting"},
{"id": "fake-setting"},
{},
],
"presets": [{
"settings": {
"█": "",
}
}]
}
{% endschema %}
`;
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
const completions = await jsonLanguageService.completions(params);
describe('section file with valid schema', () => {
const tests = [
{
label: 'preset',
schemaTemplate: (setting: object) => {
return {
presets: [setting],
};
},
},
{
label: 'default',
schemaTemplate: (setting: object) => {
return {
default: setting,
};
},
},
];

assert(isCompletionList(completions));
expect(completions.items).to.have.lengthOf(2);
expect(completions.items.map((item) => item.label)).to.include.members([
`"custom-setting"`,
`"fake-setting"`,
]);
});
for (const test of tests) {
describe(`${test.label} settings`, () => {
it(`completes ${test.label} setting property when settings exist`, async () => {
const schema = {
settings: [{ id: 'custom-setting' }, { id: 'fake-setting' }, {}],
...test.schemaTemplate({
settings: {
'█': '',
},
}),
};
const source = `
{% schema %}
${JSON.stringify(schema)}
{% endschema %}
`;
const params = getRequestParams(documentManager, 'sections/section.liquid', source);
const completions = await jsonLanguageService.completions(params);

it('offers no suggestions for preset setting property when there are no settings', async () => {
const source = `
{% schema %}
{
"presets": [{
"settings": {
"█": "",
}
}]
}
{% endschema %}
`;
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
const completions = await jsonLanguageService.completions(params);
assert(isCompletionList(completions));
expect(completions.items).to.have.lengthOf(2);
expect(completions.items.map((item) => item.label)).to.include.members([
`"custom-setting"`,
`"fake-setting"`,
]);
});

assert(isCompletionList(completions));
expect(completions.items).to.have.lengthOf(0);
expect(completions.items.map((item) => item.label)).to.include.members([]);
});
it(`offers no suggestions for ${test.label} setting property when there are no settings`, async () => {
const schema = {
...test.schemaTemplate({
settings: {
'█': '',
},
}),
};
const source = `
{% schema %}
${JSON.stringify(schema)}
{% endschema %}
`;
const params = getRequestParams(documentManager, 'sections/section.liquid', source);
const completions = await jsonLanguageService.completions(params);

it('offers presets setting completion with docs from setting.label', async () => {
const source = `
{% schema %}
{
"settings": [
{"id": "custom-setting", "label": "Custom Setting"},
{"id": "fake-setting", "label": "t:general.fake"},
],
"presets": [{
"settings": {
"█": "",
assert(isCompletionList(completions));
expect(completions.items).to.have.lengthOf(0);
expect(completions.items.map((item) => item.label)).to.include.members([]);
});

it(`offers ${test.label} setting completion with docs from setting.label`, async () => {
const schema = {
settings: [
{ id: 'custom-setting', label: 'Custom Setting' },
{ id: 'fake-setting', label: 't:general.fake' },
{},
],
...test.schemaTemplate({
settings: {
'█': '',
},
}),
};
const source = `
{% schema %}
${JSON.stringify(schema)}
{% endschema %}
`;
const params = getRequestParams(documentManager, 'sections/section.liquid', source);
const completions = await jsonLanguageService.completions(params);

assert(isCompletionList(completions));
expect(completions.items).to.have.lengthOf(2);
expect(completions.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
documentation: { kind: 'markdown', value: 'Custom Setting' },
}),
expect.objectContaining({
documentation: { kind: 'markdown', value: 'Fake Setting' },
}),
]),
);
});
});
}

describe('block file with valid schema', () => {
it(`offers no suggestions for default setting property when file is block`, async () => {
const source = `
{% schema %}
{
"settings": [
{"id": "custom-setting"},
{"id": "fake-setting"},
{},
],
"default": {
"settings": {
"█": ""
}
}
}]
}
{% endschema %}
`;
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
const completions = await jsonLanguageService.completions(params);
}
{% endschema %}
`;
const params = getRequestParams(documentManager, 'blocks/block.liquid', source);
const completions = await jsonLanguageService.completions(params);

assert(isCompletionList(completions));
expect(completions.items).to.have.lengthOf(2);
expect(completions.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
documentation: { kind: 'markdown', value: 'Custom Setting' },
}),
expect.objectContaining({
documentation: { kind: 'markdown', value: 'Fake Setting' },
}),
]),
);
assert(isCompletionList(completions));
expect(completions.items).to.have.lengthOf(0);
expect(completions.items.map((item) => item.label)).to.include.members([]);
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import { isError, SourceCodeType } from '@shopify/theme-check-common';
import { JSONPath } from 'vscode-json-languageservice';
import { JSONCompletionItem } from 'vscode-json-languageservice/lib/umd/jsonContributions';
import { RequestContext } from '../../RequestContext';
import { fileMatch } from '../../utils';
import { fileMatch, isBlockFile, isSectionFile } from '../../utils';
import { JSONCompletionProvider } from '../JSONCompletionProvider';
import { isSectionOrBlockSchema } from './BlockTypeCompletionProvider';
import { CompletionItemKind } from 'vscode-languageserver-protocol';
import { GetTranslationsForURI, renderTranslation, translationValue } from '../../../translations';

/**
* The ReferencedSettingPropertyCompletionProvider offers property completions of the
* `presets.[].settings.[]` objects inside section and theme block `{% schema %}` tags.
* The ReferencedSettingPropertyCompletionProvider offers property completions for:
* - `presets.[].settings.[]` objects inside `{% schema %}` tag in sections and blocks
* - `default.settings` object inside `{% schema %}` tag in sections
*
* @example
* {% schema %}
Expand All @@ -22,7 +23,12 @@ import { GetTranslationsForURI, renderTranslation, translationValue } from '../.
* { "█" },
* ]
* },
* ]
* ],
* "default": {
* "settings": {
* "█"
* }
* }
* }
* {% endschema %}
*/
Expand All @@ -32,10 +38,16 @@ export class ReferencedSettingPropertyCompletionProvider implements JSONCompleti
constructor(public getDefaultSchemaTranslations: GetTranslationsForURI) {}

async completeProperty(context: RequestContext, path: JSONPath): Promise<JSONCompletionItem[]> {
if (context.doc.type !== SourceCodeType.LiquidHtml) return [];

// section files can have schemas with `presets` and `default`
// block files can have schemas with `presets` only
if (
!fileMatch(context.doc.uri, this.uriPatterns) ||
context.doc.type !== SourceCodeType.LiquidHtml ||
!isPresetSettingsPath(path)
!(
isSectionFile(context.doc.uri) &&
(isPresetSettingsPath(path) || isDefaultSettingsPath(path))
) &&
!(isBlockFile(context.doc.uri) && isPresetSettingsPath(path))
) {
return [];
}
Expand Down Expand Up @@ -100,5 +112,9 @@ export class ReferencedSettingPropertyCompletionProvider implements JSONCompleti
}

function isPresetSettingsPath(path: JSONPath) {
return path.at(0) === 'presets' && path.at(2) === 'settings';
return path.length === 3 && path.at(0) === 'presets' && path.at(2) === 'settings';
}

function isDefaultSettingsPath(path: JSONPath) {
return path.length === 2 && path.at(0) === 'default' && path.at(1) === 'settings';
}
Loading

0 comments on commit e969f23

Please sign in to comment.