Skip to content

Commit

Permalink
[typespec-vscode] Make 'convert to object value' code fix recursive (#…
Browse files Browse the repository at this point in the history
…5342)

Fix :  #4613

There will be two quick fixes for this issue, and only by clicking on
the quick fix of the entire expression can you get the desired result;
If you click on the quick fix for a partial expression, because there is
no recursive part in the middle of the partial expression, you need to
click twice to get the desired result

---------

Co-authored-by: Rodge Fu <[email protected]>
Co-authored-by: Timothee Guerin <[email protected]>
  • Loading branch information
3 people authored Jan 22, 2025
1 parent 0d157d4 commit a750945
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: feature
packages:
- "@typespec/compiler"
---

Convert model/tuple expression to value code fix is applied to the entire value.
6 changes: 4 additions & 2 deletions packages/compiler/src/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { DuplicateTracker } from "../utils/duplicate-tracker.js";
import { MultiKeyMap, Mutable, createRekeyableMap, isArray, mutate } from "../utils/misc.js";
import { createSymbol, getSymNode } from "./binder.js";
import { createChangeIdentifierCodeFix } from "./compiler-code-fixes/change-identifier.codefix.js";
import { createModelToObjectValueCodeFix } from "./compiler-code-fixes/model-to-object-literal.codefix.js";
import { createTupleToArrayValueCodeFix } from "./compiler-code-fixes/tuple-to-array-value.codefix.js";
import {
createModelToObjectValueCodeFix,
createTupleToArrayValueCodeFix,
} from "./compiler-code-fixes/convert-to-value.codefix.js";
import { getDeprecationDetails, markDeprecated } from "./deprecation.js";
import { ProjectionError, compilerAssert, ignoreDiagnostics } from "./diagnostics.js";
import { validateInheritanceDiscriminatedUnions } from "./helpers/discriminator-utils.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { defineCodeFix, getSourceLocation } from "../diagnostics.js";
import {
SyntaxKind,
type CodeFixEdit,
type ModelExpressionNode,
type TupleExpressionNode,
} from "../types.js";

/**
* Quick fix that convert a tuple to an array value.
*/
export function createTupleToArrayValueCodeFix(node: TupleExpressionNode) {
return defineCodeFix({
id: "tuple-to-array-value",
label: `Convert to an array value \`#[]\``,
fix: (context) => {
const result: CodeFixEdit[] = [];

addCreatedCodeFixResult(node);
createChildTupleToArrValCodeFix(node, addCreatedCodeFixResult);

return result;

function addCreatedCodeFixResult(node: ModelExpressionNode | TupleExpressionNode) {
const location = getSourceLocation(node);
result.push(context.prependText(location, "#"));
}
},
});
}

/**
* Quick fix that convert a model expression to an object value.
*/
export function createModelToObjectValueCodeFix(node: ModelExpressionNode) {
return defineCodeFix({
id: "model-to-object-value",
label: `Convert to an object value \`#{}\``,
fix: (context) => {
const result: CodeFixEdit[] = [];

addCreatedCodeFixResult(node);
createChildModelToObjValCodeFix(node, addCreatedCodeFixResult);

return result;

function addCreatedCodeFixResult(node: ModelExpressionNode | TupleExpressionNode) {
const location = getSourceLocation(node);
result.push(context.prependText(location, "#"));
}
},
});
}

function createChildTupleToArrValCodeFix(
node: TupleExpressionNode,
addCreatedCodeFixResult: (node: ModelExpressionNode | TupleExpressionNode) => void,
) {
for (const childNode of node.values) {
if (childNode.kind === SyntaxKind.ModelExpression) {
addCreatedCodeFixResult(childNode);
createChildModelToObjValCodeFix(childNode, addCreatedCodeFixResult);
} else if (childNode.kind === SyntaxKind.TupleExpression) {
addCreatedCodeFixResult(childNode);
createChildTupleToArrValCodeFix(childNode, addCreatedCodeFixResult);
}
}
}

function createChildModelToObjValCodeFix(
node: ModelExpressionNode,
addCreatedCodeFixResult: (node: ModelExpressionNode | TupleExpressionNode) => void,
) {
for (const prop of node.properties.values()) {
if (prop.kind === SyntaxKind.ModelProperty) {
const childNode = prop.value;

if (childNode.kind === SyntaxKind.ModelExpression) {
addCreatedCodeFixResult(childNode);
createChildModelToObjValCodeFix(childNode, addCreatedCodeFixResult);
} else if (childNode.kind === SyntaxKind.TupleExpression) {
addCreatedCodeFixResult(childNode);
createChildTupleToArrValCodeFix(childNode, addCreatedCodeFixResult);
}
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { strictEqual } from "assert";
import { it } from "vitest";
import {
createModelToObjectValueCodeFix,
createTupleToArrayValueCodeFix,
} from "../../../src/core/compiler-code-fixes/convert-to-value.codefix.js";
import { SyntaxKind } from "../../../src/index.js";
import { expectCodeFixOnAst } from "../../../src/testing/code-fix-testing.js";

it("it change model expression to an object value", async () => {
await expectCodeFixOnAst(
`
model Foo {
a: string[] = ┆{foo: "abc"};
}
`,
(node) => {
strictEqual(node.kind, SyntaxKind.ModelExpression);
return createModelToObjectValueCodeFix(node);
},
).toChangeTo(`
model Foo {
a: string[] = #{foo: "abc"};
}
`);
});

it("it recursively changes the model expression to the corresponding object value", async () => {
await expectCodeFixOnAst(
`
@example(┆{Bar : {Baz : "Hello"}})
model Foo { Bar : Bar; }
model Bar { Baz : string }
`,
(node) => {
strictEqual(node.kind, SyntaxKind.ModelExpression);
return createModelToObjectValueCodeFix(node);
},
).toChangeTo(`
@example(#{Bar : #{Baz : "Hello"}})
model Foo { Bar : Bar; }
model Bar { Baz : string }
`);
});

it("it recursively changes the complex model expression to the corresponding object value or array value", async () => {
await expectCodeFixOnAst(
`
@example(┆{ Bar: [ {Baz: "Hello"}, [ "foo" ] ] })
model Foo { Bar : Array<Bar|Array<string>>; }
model Bar { Baz : string }
`,
(node) => {
strictEqual(node.kind, SyntaxKind.ModelExpression);
return createModelToObjectValueCodeFix(node);
},
).toChangeTo(`
@example(#{ Bar: #[ #{Baz: "Hello"}, #[ "foo" ] ] })
model Foo { Bar : Array<Bar|Array<string>>; }
model Bar { Baz : string }
`);
});

it("it change tuple to a array value", async () => {
await expectCodeFixOnAst(
`
model Foo {
a: string[] = ┆["abc"];
}
`,
(node) => {
strictEqual(node.kind, SyntaxKind.TupleExpression);
return createTupleToArrayValueCodeFix(node);
},
).toChangeTo(`
model Foo {
a: string[] = #["abc"];
}
`);
});

it("it recursively changes tuple to the corresponding array value", async () => {
await expectCodeFixOnAst(
`
model Tuple {
tuple: [ string, [ string ]] = ┆["foo", ["bar"]];
}
`,
(node) => {
strictEqual(node.kind, SyntaxKind.TupleExpression);
return createTupleToArrayValueCodeFix(node);
},
).toChangeTo(`
model Tuple {
tuple: [ string, [ string ]] = #["foo", #["bar"]];
}
`);
});

it("it recursively changes the complex tuple to the corresponding object value or array value", async () => {
await expectCodeFixOnAst(
`
model Bar { Baz : string }
model Tuple {
tuple: [ string, [ string,Bar ]] = ┆["foo", ["bar",{Baz: "Hello"}]];
}
`,
(node) => {
strictEqual(node.kind, SyntaxKind.TupleExpression);
return createTupleToArrayValueCodeFix(node);
},
).toChangeTo(`
model Bar { Baz : string }
model Tuple {
tuple: [ string, [ string,Bar ]] = #["foo", #["bar",#{Baz: "Hello"}]];
}
`);
});

This file was deleted.

This file was deleted.

0 comments on commit a750945

Please sign in to comment.