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>

87 lines 4.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContentForCompletionProvider = void 0; const liquid_html_parser_1 = require("@shopify/liquid-html-parser"); const vscode_languageserver_1 = require("vscode-languageserver"); class ContentForCompletionProvider { constructor() { } async completions(params) { if (!params.completionContext) return []; const { document } = params; const doc = document.textDocument; const { node, ancestors } = params.completionContext; const parentNode = ancestors.at(-1); if (!node || !parentNode || node.type !== liquid_html_parser_1.NodeTypes.String || parentNode.type !== liquid_html_parser_1.NodeTypes.ContentForMarkup) { return []; } const options = [ { keyword: 'block', description: 'Renders a static theme block within `sections` or `theme blocks`.\n', syntax: "content_for 'block', type: '$1', id: '$2'", }, { keyword: 'blocks', description: `Renders block elements within sections or other blocks as configured in the JSON template or section groups. See [theme blocks](https://shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks) to see how to create theme blocks that can be used this way.\n`, syntax: `content_for 'blocks'`, }, ]; const partial = node.value; const isInLiquidLiquidTag = ancestors.some((node) => node.type === liquid_html_parser_1.NodeTypes.LiquidTag && node.name === 'liquid'); const startPosition = node.position.start + 1; // after the quote const endPosition = indexOf(document.source, // We want to maintain trailing whitespace to whatever it was before the completion isInLiquidLiquidTag ? / *\n/g : /\s*-?%}/gm, startPosition + partial.length); const hasMarkup = document.source .slice(startPosition + partial.length, endPosition) .replace(/^['"]/, '') .trim() !== ''; const shouldCompleteSyntax = endPosition !== -1 && !hasMarkup; return options .filter(({ keyword }) => keyword.startsWith(partial)) .map(({ keyword, description, syntax }) => { const item = { label: keyword, kind: vscode_languageserver_1.CompletionItemKind.Keyword, insertTextFormat: vscode_languageserver_1.InsertTextFormat.PlainText, documentation: { kind: 'markdown', value: description, }, }; if (shouldCompleteSyntax) { const snippetText = getSnippetText(node, syntax); item.insertTextFormat = vscode_languageserver_1.InsertTextFormat.Snippet; item.textEdit = vscode_languageserver_1.TextEdit.replace(vscode_languageserver_1.Range.create(doc.positionAt(startPosition), doc.positionAt(endPosition)), snippetText); } return item; }); } } exports.ContentForCompletionProvider = ContentForCompletionProvider; function getSnippetText(node, syntax) { // Language clients don't like it when the text edit starts before the word being completed // So we make the snippet text start with the word being completed return (syntax .replace(/^content_for '/, '') // use the same quote type as the original string everywhere in the snippet .replace(node.single ? /"/g : /'/g, node.single ? "'" : '"')); } /** * String.prototype.indexOf does not accept RegExp args... * String.prototype.search does not accept fromIndex args... * * We want both. */ function indexOf(string, searchValue, fromIndex) { searchValue.lastIndex = fromIndex; const match = searchValue.exec(string); return match ? match.index : -1; } //# sourceMappingURL=ContentForCompletionProvider.js.map