UNPKG

@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>

126 lines 6.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JSONLanguageService = void 0; const liquid_html_parser_1 = require("@shopify/liquid-html-parser"); const theme_check_common_1 = require("@shopify/theme-check-common"); const vscode_json_languageservice_1 = require("vscode-json-languageservice"); const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument"); const JSONContributions_1 = require("./JSONContributions"); class JSONLanguageService { constructor(documentManager, jsonValidationSet, getDefaultSchemaTranslations, getModeForURI, getThemeBlockNames, getThemeBlockSchema) { this.documentManager = documentManager; this.jsonValidationSet = jsonValidationSet; this.getDefaultSchemaTranslations = getDefaultSchemaTranslations; this.getModeForURI = getModeForURI; this.getThemeBlockNames = getThemeBlockNames; this.getThemeBlockSchema = getThemeBlockSchema; this.isValidSchema = async (uri, jsonString) => { const mode = await this.getModeForURI(uri); const service = this.services[mode]; if (!service) return false; return (0, theme_check_common_1.isValid)(service, uri, jsonString); }; this.services = Object.fromEntries(theme_check_common_1.Modes.map((mode) => [mode, null])); this.schemas = {}; } async setup(clientCapabilities) { await Promise.all(theme_check_common_1.Modes.map(async (mode) => { const schemas = await this.jsonValidationSet.schemas(mode); for (const schema of schemas) { this.schemas[schema.uri] = schema; } if (!schemas.length) return; const service = (0, vscode_json_languageservice_1.getLanguageService)({ clientCapabilities, // Map URIs to schemas without making network requests. Removes the // network dependency. schemaRequestService: this.getSchemaForURI.bind(this), // This is how we make sure that our "$ref": "./inputSettings.json" in // our JSON schemas resolve correctly. workspaceContext: { resolveRelativePath: (relativePath, resource) => { const url = new URL(relativePath, resource); return url.toString(); }, }, contributions: [ new JSONContributions_1.JSONContributions(this.documentManager, this.getDefaultSchemaTranslations, this.getThemeBlockNames, this.getThemeBlockSchema), ], }); service.configure({ // This is what we use to map file names to JSON schemas. Without // this, we'd need folks to use the `$schema` field in their JSON // blobs. That ain't fun nor is going to happen. schemas: schemas.map((schemaDefinition) => ({ uri: schemaDefinition.uri, fileMatch: schemaDefinition.fileMatch, })), }); this.services[mode] = service; })); } async completions(params) { const mode = await this.getModeForURI(params.textDocument.uri); const service = this.services[mode]; if (!service) return null; const documents = this.getDocuments(params, service); if (!documents) return null; const [jsonTextDocument, jsonDocument] = documents; return service.doComplete(jsonTextDocument, params.position, jsonDocument); } async hover(params) { const mode = await this.getModeForURI(params.textDocument.uri); const service = this.services[mode]; if (!service) return null; const documents = this.getDocuments(params, service); if (!documents) return null; const [jsonTextDocument, jsonDocument] = documents; return service.doHover(jsonTextDocument, params.position, jsonDocument); } getDocuments(params, service) { const document = this.documentManager.get(params.textDocument.uri); if (!document) return null; switch (document.type) { case theme_check_common_1.SourceCodeType.JSON: { const jsonTextDocument = document.textDocument; const jsonDocument = service.parseJSONDocument(jsonTextDocument); return [jsonTextDocument, jsonDocument]; } case theme_check_common_1.SourceCodeType.LiquidHtml: { if (document.ast instanceof Error) return null; const textDocument = document.textDocument; const offset = textDocument.offsetAt(params.position); const [_, ancestors] = (0, theme_check_common_1.findCurrentNode)(document.ast, offset); const schema = ancestors.find((node) => node.type === liquid_html_parser_1.NodeTypes.LiquidRawTag && node.name === 'schema'); if (!schema) return null; const schemaLineNumber = textDocument.positionAt(schema.blockStartPosition.end).line; // Hacking away "same line numbers" here by prefixing the file with newlines // This way params.position will be at the same line number in this fake jsonTextDocument // Which means that the completions will be at the same line number in the Liquid document const jsonString = Array(schemaLineNumber).fill('\n').join('') + schema.source.slice(schema.blockStartPosition.end, schema.blockEndPosition.start); const jsonTextDocument = vscode_languageserver_textdocument_1.TextDocument.create(textDocument.uri, 'json', textDocument.version, jsonString); const jsonDocument = service.parseJSONDocument(jsonTextDocument); return [jsonTextDocument, jsonDocument]; } } } async getSchemaForURI(uri) { var _a; const schema = (_a = this.schemas[uri]) === null || _a === void 0 ? void 0 : _a.schema; if (!schema) return `Could not get schema for '${uri}'`; return schema; } } exports.JSONLanguageService = JSONLanguageService; //# sourceMappingURL=JSONLanguageService.js.map