From f8584a59bf2a53d6a6d898eae18628b9f129e69c Mon Sep 17 00:00:00 2001 From: "Zhonglei Ma (WICRESOFT NORTH AMERICA LTD)" Date: Thu, 2 Jan 2025 17:25:42 +0800 Subject: [PATCH 1/2] Converting JSON to values in code fixes --- .../json-to-values.codefix.ts | 39 +++++++++++++++++++ packages/compiler/src/lib/decorators.ts | 15 +++++++ .../json-to-values.codefix.test.ts | 23 +++++++++++ 3 files changed, 77 insertions(+) create mode 100644 packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts create mode 100644 packages/compiler/test/core/compiler-code-fixes/json-to-values.codefix.test.ts diff --git a/packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts b/packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts new file mode 100644 index 0000000000..7af883ae42 --- /dev/null +++ b/packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts @@ -0,0 +1,39 @@ +import { defineCodeFix, getSourceLocation } from "../diagnostics.js"; +import { CodeFixEdit, DiagnosticTarget, ObjectLiteralNode, SyntaxKind, Type } from "../types.js"; + +export function createJsonToValuesCodeFix(diagnosticTarget: DiagnosticTarget | Node) { + return defineCodeFix({ + id: "json-to-values", + label: `Convert json to values`, + fix: (context) => { + const result: CodeFixEdit[] = []; + + if ( + "kind" in diagnosticTarget && + typeof diagnosticTarget.kind === "number" && + diagnosticTarget.kind === SyntaxKind.ObjectLiteral && + diagnosticTarget.properties.length === 2 + ) { + // Test use + createCodeFix(diagnosticTarget); + } else { + // Actual use + const targetNode = (diagnosticTarget as Type).node; + if (targetNode?.kind === SyntaxKind.ObjectLiteral && targetNode.properties.length === 2) { + createCodeFix(targetNode); + } + } + + return result; + + function createCodeFix(node: ObjectLiteralNode) { + const firstNode = node.properties[0]; + const lastNode = node.properties[1]; + const location = getSourceLocation(firstNode); + + const newText = location.file.text.slice(location.pos + 1, lastNode.pos - 1); + result.push(context.replaceText(location, newText)); + } + }, + }); +} diff --git a/packages/compiler/src/lib/decorators.ts b/packages/compiler/src/lib/decorators.ts index 40f15e7867..f331133e0d 100644 --- a/packages/compiler/src/lib/decorators.ts +++ b/packages/compiler/src/lib/decorators.ts @@ -33,6 +33,7 @@ import type { WithoutDefaultValuesDecorator, WithoutOmittedPropertiesDecorator, } from "../../generated-defs/TypeSpec.js"; +import { createJsonToValuesCodeFix } from "../core/compiler-code-fixes/json-to-values.codefix.js"; import { getPropertyType, isIntrinsicType, @@ -40,6 +41,7 @@ import { } from "../core/decorator-utils.js"; import { getDeprecationDetails, markDeprecated } from "../core/deprecation.js"; import { + NoTarget, Numeric, StdTypeName, compilerAssert, @@ -98,6 +100,7 @@ import { UnionVariant, Value, } from "../core/types.js"; +import { mutate } from "../utils/misc.js"; import { setKey } from "./key.js"; import { useStateMap, useStateSet } from "./utils.js"; @@ -1396,6 +1399,18 @@ function checkExampleValid( diagnosticTarget, ); if (!assignable) { + // add a codefix to convert json to values , issue #4612 + for (const diagnostic of diagnostics) { + if ( + diagnostic.severity === "error" && + diagnostic.target !== NoTarget && + diagnostic.code === "missing-property" + ) { + mutate(diagnostic).codefixes ??= []; + mutate(diagnostic.codefixes).push(createJsonToValuesCodeFix(diagnostic.target)); + } + } + program.reportDiagnostics(diagnostics); } return assignable; diff --git a/packages/compiler/test/core/compiler-code-fixes/json-to-values.codefix.test.ts b/packages/compiler/test/core/compiler-code-fixes/json-to-values.codefix.test.ts new file mode 100644 index 0000000000..9fad2dc2c1 --- /dev/null +++ b/packages/compiler/test/core/compiler-code-fixes/json-to-values.codefix.test.ts @@ -0,0 +1,23 @@ +import { strictEqual } from "assert"; +import { describe, it } from "vitest"; +import { createJsonToValuesCodeFix } from "../../../src/core/compiler-code-fixes/json-to-values.codefix.js"; +import { SyntaxKind } from "../../../src/index.js"; +import { expectCodeFixOnAst } from "../../../src/testing/code-fix-testing.js"; + +describe("CodeFix: convert-json-to-value", () => { + it("it's missing properties, remove the quotes around property names that contain quotes", async () => { + await expectCodeFixOnAst( + ` + @example(┆#{"Baz": "Hello"}) + model FooBar { Baz : string; } + `, + (node) => { + strictEqual(node.kind, SyntaxKind.ObjectLiteral); + return createJsonToValuesCodeFix(node); + }, + ).toChangeTo(` + @example(#{Baz: "Hello"}) + model FooBar { Baz : string; } + `); + }); +}); From bef7e31d120a1f60d49a42a403023d1a27631b02 Mon Sep 17 00:00:00 2001 From: "Zhonglei Ma (WICRESOFT NORTH AMERICA LTD)" Date: Fri, 3 Jan 2025 09:29:39 +0800 Subject: [PATCH 2/2] updated --- .../json-to-values.codefix.ts | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts b/packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts index 7af883ae42..6a0ec9d7cf 100644 --- a/packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts +++ b/packages/compiler/src/core/compiler-code-fixes/json-to-values.codefix.ts @@ -1,39 +1,28 @@ +import { CharCode } from "../charcode.js"; import { defineCodeFix, getSourceLocation } from "../diagnostics.js"; -import { CodeFixEdit, DiagnosticTarget, ObjectLiteralNode, SyntaxKind, Type } from "../types.js"; +import { DiagnosticTarget } from "../types.js"; -export function createJsonToValuesCodeFix(diagnosticTarget: DiagnosticTarget | Node) { +export function createJsonToValuesCodeFix(node: DiagnosticTarget) { return defineCodeFix({ id: "json-to-values", label: `Convert json to values`, fix: (context) => { - const result: CodeFixEdit[] = []; - - if ( - "kind" in diagnosticTarget && - typeof diagnosticTarget.kind === "number" && - diagnosticTarget.kind === SyntaxKind.ObjectLiteral && - diagnosticTarget.properties.length === 2 - ) { - // Test use - createCodeFix(diagnosticTarget); - } else { - // Actual use - const targetNode = (diagnosticTarget as Type).node; - if (targetNode?.kind === SyntaxKind.ObjectLiteral && targetNode.properties.length === 2) { - createCodeFix(targetNode); + const location = getSourceLocation(node); + const text = location.file.text; + let pos = location.pos; + const end = location.end; + const range: number[] = []; + while (pos < end) { + if (text.charCodeAt(pos) === CharCode.DoubleQuote) { + range.push(pos); } + if (range.length === 2) { + break; + } + pos++; } - - return result; - - function createCodeFix(node: ObjectLiteralNode) { - const firstNode = node.properties[0]; - const lastNode = node.properties[1]; - const location = getSourceLocation(firstNode); - - const newText = location.file.text.slice(location.pos + 1, lastNode.pos - 1); - result.push(context.replaceText(location, newText)); - } + const replaceText = text.slice(range[0] + 1, range[1]); + return context.replaceText({ ...location, pos: range[0], end: range[1] + 1 }, replaceText); }, }); }