diff --git a/packages/language-service/src/language-plugin.ts b/packages/language-service/src/language-plugin.ts index 92b43cd..abc0fc6 100644 --- a/packages/language-service/src/language-plugin.ts +++ b/packages/language-service/src/language-plugin.ts @@ -213,36 +213,64 @@ function* getTempblotEmbeddedCodes( return; } - // Create JSON output with interpolations replaced by placeholder values - const { transformedText, jsonMappings } = createJsonWithMappings( + const outputLanguageId = getOutputLanguageId(output.attributes.lang); + const { transformedText, mappings } = createOutputWithMappings( outputText, interpolationsData, output.startTagEnd, + outputLanguageId, ); - // TODO: Make generic and not tied to JSON yield { - id: "output_json", - languageId: "json", + id: "output", + languageId: outputLanguageId, snapshot: { getText: (start, end) => transformedText.substring(start, end), getLength: () => transformedText.length, getChangeRange: () => undefined, }, - mappings: jsonMappings, + mappings, embeddedCodes: [], }; } -function createJsonWithMappings( +function getOutputLanguageId(lang: string | undefined): string { + switch (lang) { + case undefined: + case "html": + return "html"; + case "md": + return "markdown"; + case "js": + return "javascript"; + case "jsx": + return "javascriptreact"; + case "ts": + return "typescript"; + case "tsx": + return "typescriptreact"; + case "txt": + return "plaintext"; + case "gql": + return "graphql"; + case "coffee": + return "coffeescript"; + default: + return lang; + } +} + +function createOutputWithMappings( outputText: string, interpolationsData: InterpolationData[], outputStartOffset: number, -): { transformedText: string; jsonMappings: CodeMapping[] } { + languageId: string, +): { transformedText: string; mappings: CodeMapping[] } { const mappings: CodeMapping[] = []; let transformedText = ""; let lastOffset = 0; let generatedOffset = 0; + const interpolationPlaceholder = getInterpolationPlaceholder(languageId); // Process each interpolation for (const interp of interpolationsData) { @@ -266,10 +294,8 @@ function createJsonWithMappings( generatedOffset += beforeText.length; } - // Replace interpolation with null placeholder for JSON validity - const placeholder = "null"; - transformedText += placeholder; - generatedOffset += placeholder.length; + transformedText += interpolationPlaceholder; + generatedOffset += interpolationPlaceholder.length; lastOffset = interp.fullEnd; } @@ -293,5 +319,24 @@ function createJsonWithMappings( transformedText += remainingText; } - return { transformedText, jsonMappings: mappings }; + return { transformedText, mappings }; +} + +function getInterpolationPlaceholder(languageId: string): string { + switch (languageId) { + case "json": + case "jsonc": + case "json5": + return "null"; + case "css": + case "scss": + case "less": + case "sass": + case "stylus": + case "postcss": + case "toml": + return "0"; + default: + return "tempblot"; + } } diff --git a/packages/language-service/tests/language-plugin.spec.ts b/packages/language-service/tests/language-plugin.spec.ts index 08d7192..08e074a 100644 --- a/packages/language-service/tests/language-plugin.spec.ts +++ b/packages/language-service/tests/language-plugin.spec.ts @@ -38,7 +38,7 @@ const value: number = 1; expect(combinedContext?.snapshot.getText(0, combinedContext.snapshot.getLength())) .toContain("const value: number = 1;"); expect(virtualCode.embeddedCodes.map((code) => code.id)).not.toContain( - "output_json", + "output", ); }); @@ -51,16 +51,49 @@ test("creates output embedded code for output-only files", () => { const combinedContext = virtualCode.embeddedCodes.find( (code) => code.id === "combined_context", ); - const outputJson = virtualCode.embeddedCodes.find( - (code) => code.id === "output_json", + const output = virtualCode.embeddedCodes.find( + (code) => code.id === "output", ); expect(combinedContext?.snapshot.getText(0, combinedContext.snapshot.getLength())) .toContain("(1 + 1);"); - expect(outputJson?.snapshot.getText(0, outputJson.snapshot.getLength())) + expect(output?.languageId).toBe("json"); + expect(output?.snapshot.getText(0, output.snapshot.getLength())) .toContain('{"value": null}'); }); +test("creates output embedded code from non-JSON output languages", () => { + const source = ``; + + const virtualCode = createVirtualCode(source); + const output = virtualCode.embeddedCodes.find((code) => code.id === "output"); + + expect(output?.languageId).toBe("html"); + expect(output?.snapshot.getText(0, output.snapshot.getLength())) + .toContain("