Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions language-server/src/evaluation/VscodeAnnotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,9 @@ export const VscodeAnnotation = <T extends Constructor<Extended>>(Base: T) => cl
?? this.unknownKeyword("defaultSnippets", `${metaSchemaUri}#/properties/defaultSnippets`, ast)
?? []) as DefaultSnippet[];
}

doNotSuggest(): boolean {
return ((this.keywords["https://microsoft.com/keyword/doNotSuggest"]
?? this.unknownKeyword("doNotSuggest", `${metaSchemaUri}#/properties/doNotSuggest`, ast)) === true);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -1388,4 +1388,269 @@ describe("Property completions", () => {
]);
});
});

describe("doNotSuggest", () => {
beforeEach(async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"properties": {
"name": { "type": "string", "doNotSuggest": true },
"age": { "type": "integer", "doNotSuggest": false },
"fullName": { "type": "string" }
}
}
}
}`);

await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"value": {
""
}
}`);
});

test("a property with doNotSuggest isn't suggested", async () => {
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 3, character: 11 }
}) as CompletionItem[];

expect(completions).toMatchObject([
{ label: "age" },
{ label: "fullName" }
]);
});

test("a property doNotSuggest through $ref or allOf isn't suggested", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"properties": {
"foo": { "$ref": "#/$defs/hidden" },
"bar": { "allOf": [{ "$ref": "#/$defs/hidden" }] },
"baz": { "type": "string" }
}
}
},
"$defs": {
"hidden": { "doNotSuggest": true }
}
}`);
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 3, character: 11 }
}) as CompletionItem[];

expect(completions).toMatchObject([
{ label: "baz" }
]);
});

test("a property with doNotSuggest in an anyOf alternative is still suggested", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"properties": {
"name": {
"anyOf": [
{ "doNotSuggest": true },
{ "type": "string" }
]
}
}
}
}
}`);
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 3, character: 11 }
}) as CompletionItem[];

expect(completions).toMatchObject([
{ label: "name" }
]);
});

test("a property is still suggested when doNotSuggest isn't a boolean", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"properties": {
"name": { "type": "string", "doNotSuggest": "yes" }
}
}
}
}`);
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 3, character: 11 }
}) as CompletionItem[];

expect(completions).toMatchObject([
{ label: "name" }
]);
});

test("doNotSuggest from a dialect that includes the vscode vocabulary", async () => {
await client.writeDocument("meta-schema.json", `{
"$id": "https://example.com/dialect/vscode",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
"https://json-schema.org/draft/2020-12/vocab/validation": true,
"https://microsoft.com/vocab/vscode": true
},
"$dynamicAnchor": "meta",
"allOf": [
{ "$ref": "https://json-schema.org/draft/2020-12/schema" },
{ "$ref": "https://microsoft.com/meta/vscode" }
]
}`);

fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://example.com/dialect/vscode",
"type": "object",
"properties": {
"value": {
"properties": {
"name": { "type": "string", "doNotSuggest": true },
"age": { "type": "integer" }
}
}
}
}`);
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 3, character: 11 }
}) as CompletionItem[];

expect(completions).toMatchObject([
{ label: "age" }
]);
});

test("a property with doNotSuggest in every anyOf alternative isn't suggested", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"anyOf": [
{
"properties": {
"name": { "doNotSuggest": true }
}
},
{
"properties": {
"name": { "doNotSuggest": true }
}
}
]
}
}
}`);
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 3, character: 11 }
}) as CompletionItem[];

expect(completions).toMatchObject([]);
});

describe("oneOf alternatives", () => {
beforeEach(async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"value": {
"oneOf": [
{
"properties": {
"kind": { "const": "cat" },
"name": { "doNotSuggest": true },
"meow": true
},
"required": ["kind"]
},
{
"properties": {
"kind": { "const": "dog" },
"name": { "type": "string" },
"bark": true
},
"required": ["kind"]
}
]
}
}
}`);
});

test("a property is suggested once the discriminant selects an alternative without doNotSuggest", async () => {
await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"value": {
"kind": "dog",
""
}
}`);
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 4, character: 13 }
}) as CompletionItem[];

expect(completions).toMatchObject([
{ label: "name" },
{ label: "bark" }
]);
});

test("a property isn't suggested once the discriminant selects an alternative with doNotSuggest", async () => {
await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"value": {
"kind": "cat",
""
}
}`);
const uri = await client.openDocument("instance.json");

const completions = await client.sendRequest(CompletionRequest.type, {
textDocument: { uri },
position: { line: 4, character: 13 }
}) as CompletionItem[];

expect(completions).toMatchObject([
{ label: "meow" }
]);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ export class PropertyCompletionsProvider implements CompletionsProvider {
continue;
}

const propertyLocation = JsonPointer.append(propertyName, instanceLocation);
const annotations = plugin.getLocationAnnotations(propertyLocation);
// Annotations from the parent's anyOf/oneOf alternatives are location
// annotations here, so one alternative's doNotSuggest hides the
// property until a discriminant rules that alternative out
if (annotations.some((annotation) => annotation.doNotSuggest())) {
continue;
}

const completionItem: CompletionItem = {
label: propertyName,
kind: CompletionItemKind.Property,
Expand All @@ -60,8 +69,7 @@ export class PropertyCompletionsProvider implements CompletionsProvider {
};

// A property is deprecated only if it's deprecated no matter what its value is
const propertyLocation = JsonPointer.append(propertyName, instanceLocation);
tagDeprecated(completionItem, plugin.getLocationAnnotations(propertyLocation));
tagDeprecated(completionItem, annotations);

completionItems.push(completionItem);
}
Expand Down
13 changes: 11 additions & 2 deletions language-server/src/vocabularies/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,21 @@ addKeyword({
interpret: () => true
});

addKeyword({
id: "https://microsoft.com/keyword/doNotSuggest",
compile: (schema) => Browser.value(schema),
interpret: () => true,
annotation: (value: unknown) => value
});

defineVocabulary("https://microsoft.com/vocab/vscode", {
markdownDescription: "https://microsoft.com/keyword/markdownDescription",
defaultSnippets: "https://microsoft.com/keyword/defaultSnippets",
deprecationMessage: "https://microsoft.com/keyword/deprecationMessage",
markdownDeprecationMessage: "https://microsoft.com/keyword/markdownDeprecationMessage",
errorMessage: "https://microsoft.com/keyword/errorMessage",
patternErrorMessage: "https://microsoft.com/keyword/patternErrorMessage"
patternErrorMessage: "https://microsoft.com/keyword/patternErrorMessage",
doNotSuggest: "https://microsoft.com/keyword/doNotSuggest"
});

registerSchema({
Expand All @@ -75,6 +83,7 @@ registerSchema({
deprecationMessage: { type: "string" },
markdownDeprecationMessage: { type: "string" },
errorMessage: { type: "string" },
patternErrorMessage: { type: "string" }
patternErrorMessage: { type: "string" },
doNotSuggest: { type: "boolean" }
}
});
Loading