diff --git a/src/__tests__/__fixtures__/schemas.ts b/src/__tests__/__fixtures__/schemas.ts index 4cb2769..21cf847 100644 --- a/src/__tests__/__fixtures__/schemas.ts +++ b/src/__tests__/__fixtures__/schemas.ts @@ -17,6 +17,16 @@ export const testSchema2 = { foo: { type: "string", }, + stringWithDefault: { + type: "string", + description: "a string with a default value", + default: "defaultString", + }, + bracedStringDefault: { + type: "string", + description: "a string with a default value containing braces", + default: "✨ A message from %{whom}: ✨", + }, object: { type: "object", properties: { diff --git a/src/__tests__/__helpers__/completion.ts b/src/__tests__/__helpers__/completion.ts index 6a5118e..ade1fa3 100644 --- a/src/__tests__/__helpers__/completion.ts +++ b/src/__tests__/__helpers__/completion.ts @@ -47,7 +47,7 @@ export async function expectCompletion( } = {} ) { let cur = doc.indexOf("|"), - currentSchema = conf?.schema || testSchema2; + currentSchema = conf?.schema ?? testSchema2; doc = doc.slice(0, cur) + doc.slice(cur + 1); let state = EditorState.create({ diff --git a/src/__tests__/json-completion.spec.ts b/src/__tests__/json-completion.spec.ts index 650a897..16bd661 100644 --- a/src/__tests__/json-completion.spec.ts +++ b/src/__tests__/json-completion.spec.ts @@ -2,6 +2,7 @@ import { describe, it } from "vitest"; import { expectCompletion } from "./__helpers__/completion.js"; import { MODES } from "../constants.js"; +import { testSchema3 } from "./__fixtures__/schemas.js"; describe.each([ { @@ -46,6 +47,35 @@ describe.each([ }, ], }, + { + name: "include defaults for string when available", + mode: MODES.JSON, + docs: ['{ "stringWithDefault| }'], + expectedResults: [ + { + label: "stringWithDefault", + type: "property", + detail: "string", + info: "a string with a default value", + template: '"stringWithDefault": "${defaultString}"', + }, + ], + }, + // TODO: fix the default template with braces: https://discuss.codemirror.net/t/inserting-literal-via-snippets/8136/4 + // { + // name: "include defaults for string with braces", + // mode: MODES.JSON, + // docs: ['{ "bracedStringDefault| }'], + // expectedResults: [ + // { + // label: "bracedStringDefault", + // type: "property", + // detail: "string", + // info: "a string with a default value containing braces", + // template: '"bracedStringDefault": "${✨ A message from %{whom}: ✨}"', + // }, + // ], + // }, { name: "include defaults for enum when available", mode: MODES.JSON, @@ -221,6 +251,20 @@ describe.each([ ], }, // TODO: completion for array of objects should enhance the template + { + name: "autocomplete for array of objects with filter", + mode: MODES.JSON, + docs: ['{ "arrayOfObjects": [ { "f|" } ] }'], + expectedResults: [ + { + detail: "string", + info: "", + label: "foo", + template: '"foo": "#{}"', + type: "property", + }, + ], + }, { name: "autocomplete for array of objects with items", mode: MODES.JSON, @@ -298,6 +342,28 @@ describe.each([ }, ], }, + { + name: "autocomplete for a schema with top level $ref", + mode: MODES.JSON, + docs: ['{ "| }'], + expectedResults: [ + { + type: "property", + detail: "string", + info: "", + label: "foo", + template: '"foo": "#{}"', + }, + { + type: "property", + detail: "number", + info: "", + label: "bar", + template: '"bar": #{0}', + }, + ], + schema: testSchema3, + }, // JSON5 { name: "return bare property key when no quotes are used", @@ -653,8 +719,8 @@ describe.each([ }, ], }, -])("jsonCompletion", ({ name, docs, mode, expectedResults }) => { +])("jsonCompletion", ({ name, docs, mode, expectedResults, schema }) => { it.each(docs)(`${name} (mode: ${mode})`, async (doc) => { - await expectCompletion(doc, expectedResults, { mode }); + await expectCompletion(doc, expectedResults, { mode, schema }); }); }); diff --git a/src/json-completion.ts b/src/json-completion.ts index d3f142f..08def88 100644 --- a/src/json-completion.ts +++ b/src/json-completion.ts @@ -58,7 +58,8 @@ export class JSONCompletion { this.mode = opts.mode ?? MODES.JSON; } public doComplete(ctx: CompletionContext) { - this.schema = getJSONSchema(ctx.state)!; + const s = getJSONSchema(ctx.state)!; + this.schema = this.expandSchemaProperty(s, s) ?? s; if (!this.schema) { // todo: should we even do anything without schema // without taking over the existing mode responsibilties? @@ -484,13 +485,10 @@ export class JSONCompletion { switch (this.mode) { case MODES.JSON5: return `'${prf}{${value}}'`; - break; case MODES.YAML: return `${prf}{${value}}`; - break; default: return `"${prf}{${value}}"`; - break; } } @@ -847,8 +845,8 @@ export class JSONCompletion { return [subSchema as JSONSchema7]; } - private expandSchemaProperty( - property: JSONSchema7Definition, + private expandSchemaProperty( + property: T, schema: JSONSchema7 ) { if (typeof property === "object" && property.$ref) {