@shopify/theme-language-server-common
Version:
<h1 align="center" style="position: relative;" > <br> <img src="https://github.com/Shopify/theme-check-vscode/blob/main/images/shopify_glyph.png?raw=true" alt="logo" width="141" height="160"> <br> Theme Language Server </h1>
76 lines (64 loc) • 2.55 kB
text/typescript
import { NodeTypes } from '@shopify/liquid-html-parser';
import { ObjectEntry } from '@shopify/theme-check-common';
import { CompletionItem, CompletionItemKind } from 'vscode-languageserver';
import { TypeSystem, isArrayType } from '../../TypeSystem';
import { CURSOR, LiquidCompletionParams } from '../params';
import { Provider, createCompletionItem, sortByName } from './common';
import { GetThemeSettingsSchemaForURI } from '../../settings';
const ArrayCoreProperties = ['size', 'first', 'last'] as const;
const StringCoreProperties = ['size'] as const;
export class ObjectAttributeCompletionProvider implements Provider {
constructor(
private readonly typeSystem: TypeSystem,
private readonly getThemeSettingsSchema: GetThemeSettingsSchemaForURI,
) {}
async completions(params: LiquidCompletionParams): Promise<CompletionItem[]> {
if (!params.completionContext) return [];
const { partialAst, node } = params.completionContext;
if (!node || node.type !== NodeTypes.VariableLookup) {
return [];
}
if (node.lookups.length === 0) {
// We only do lookups in this one
return [];
}
const lastLookup = node.lookups.at(-1)!;
if (lastLookup.type !== NodeTypes.String) {
// We don't complete numbers, or variable lookups
return [];
}
const partial = lastLookup.value.replace(CURSOR, '');
// Fake a VariableLookup up to the last one.
const parentLookup = { ...node };
parentLookup.lookups = [...parentLookup.lookups];
parentLookup.lookups.pop();
const parentType = await this.typeSystem.inferType(
parentLookup,
partialAst,
params.textDocument.uri,
);
if (isArrayType(parentType)) {
return completionItems(
ArrayCoreProperties.map((name) => ({ name })),
partial,
);
} else if (parentType === 'string') {
return completionItems(
StringCoreProperties.map((name) => ({ name })),
partial,
);
}
const objectMap = await this.typeSystem.objectMap(params.textDocument.uri, partialAst);
const parentTypeProperties = objectMap[parentType]?.properties || [];
return completionItems(parentTypeProperties, partial);
}
}
function completionItems(options: ObjectEntry[], partial: string) {
return options
.filter(({ name }) => name.startsWith(partial))
.sort(sortByName)
.map(toPropertyCompletionItem);
}
function toPropertyCompletionItem(object: ObjectEntry) {
return createCompletionItem(object, { kind: CompletionItemKind.Variable });
}