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
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"files.insertFinalNewline": true
"files.insertFinalNewline": true,
"coverage-gutters.coverageBaseDir": "**",
"coverage-gutters.coverageFileNames": ["clover.xml"]
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,10 @@ This approach allows you to configure the json5 mode and parse linter, as well a
import { EditorState } from "@codemirror/state";
import { linter } from "@codemirror/lint";
import { json5, json5ParseLinter, json5Language } from "codemirror-json5";
import { jsonCompletion } from "codemirror-json-schema";
import {
json5SchemaLinter,
json5SchemaHover,
json5Completion,
} from "codemirror-json-schema/json5";

const schema = {
Expand All @@ -182,7 +182,7 @@ const json5State = EditorState.create({
linter(json5SchemaLinter(schema)),
hoverTooltip(json5SchemaHover(schema)),
json5Language.data.of({
autocomplete: jsonCompletion(schema),
autocomplete: json5Completion(schema),
}),
],
});
Expand Down
21 changes: 15 additions & 6 deletions src/__tests__/__helpers__/completion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { describe, it, expect, vitest, Mock } from "vitest";

import { json, jsonLanguage } from "@codemirror/lang-json";
import { json5, json5Language } from "codemirror-json5";

import { EditorState } from "@codemirror/state";
import {
Completion,
Expand Down Expand Up @@ -38,19 +40,26 @@ type MockedCompletionResult = CompletionResult["options"][0] & {
export async function expectCompletion(
doc: string,
results: MockedCompletionResult[],
schema?: JSONSchema7,
conf: { explicit?: boolean } = {}

conf: {
explicit?: boolean;
schema?: JSONSchema7;
mode?: "json" | "json5";
} = {}
) {
let cur = doc.indexOf("|"),
currentSchema = schema || testSchema2;
currentSchema = conf?.schema || testSchema2;
doc = doc.slice(0, cur) + doc.slice(cur + 1);
const jsonMode = conf?.mode === "json5" ? json5 : json;
const jsonLang = conf?.mode === "json5" ? json5Language : jsonLanguage;

let state = EditorState.create({
doc,
selection: { anchor: cur },
extensions: [
json(),
jsonLanguage.data.of({
autocomplete: jsonCompletion(currentSchema),
jsonMode(),
jsonLang.data.of({
autocomplete: jsonCompletion(currentSchema, { mode: conf.mode }),
}),
],
});
Expand Down
183 changes: 180 additions & 3 deletions src/__tests__/json-completion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ describe("jsonCompletion", () => {
type: "property",
detail: "",
info: "an example enum with default bar",
// TODO: should this not autocomplete to default "bar"?
// template: '"enum1": "${bar}"',
template: '"enum1": #{}',
template: '"enum1": "${bar}"',
},
{
label: "enum2",
Expand Down Expand Up @@ -109,6 +107,18 @@ describe("jsonCompletion", () => {
},
]);
});
// TODO: accidentally steps up to the parent pointer
it.skip("should include insert text for nested object properties", async () => {
await expectCompletion(`{ "object": { '| } }`, [
{
detail: "string",
info: "an elegant string",
label: "foo",
template: '"foo": "#{}"',
type: "property",
},
]);
});
it("should include insert text for nested object properties with filter", async () => {
await expectCompletion('{ "object": { "f|" } }', [
{
Expand Down Expand Up @@ -162,5 +172,172 @@ describe("jsonCompletion", () => {
type: "property",
},
]);
it("should autocomplete for oneOf without quotes", async () => {
await expectCompletion('{ "oneOfObject": { | } }', [
{
detail: "string",
info: "",
label: "foo",
template: '"foo": "#{}"',
type: "property",
},
{
detail: "number",
info: "",
label: "bar",
template: '"bar": #{0}',
type: "property",
},
{
detail: "string",
info: "",
label: "apple",
template: '"apple": "#{}"',
type: "property",
},
{
detail: "number",
info: "",
label: "banana",
template: '"banana": #{0}',
type: "property",
},
]);
});
});
});

describe("json5Completion", () => {
it("should return bare property key when no quotes are used", async () => {
await expectCompletion(
"{ f| }",
[
{
label: "foo",
type: "property",
detail: "string",
info: "",
template: "foo: '#{}'",
},
],
{ mode: "json5" }
);
});
it("should return template for '", async () => {
await expectCompletion(
"{ 'one|' }",
[
{
label: "oneOfEg",
type: "property",
detail: "",
info: "an example oneOf",
template: "'oneOfEg': ",
},
{
label: "oneOfEg2",
type: "property",
detail: "",
info: "",
template: "'oneOfEg2': ",
},
{
detail: "",
info: "",
label: "oneOfObject",
template: "'oneOfObject': ",
type: "property",
},
],
{ mode: "json5" }
);
});
it("should include defaults for enum when available", async () => {
await expectCompletion(
'{ "en|" }',
[
{
label: "enum1",
type: "property",
detail: "",
info: "an example enum with default bar",
template: '"enum1": "${bar}"',
},
{
label: "enum2",
type: "property",
detail: "",
info: "an example enum without default",
template: '"enum2": #{}',
},
],
{ mode: "json5" }
);
});
it("should include defaults for boolean when available", async () => {
await expectCompletion(
"{ booleanW| }",
[
{
type: "property",
detail: "boolean",
info: "an example boolean with default",
label: "booleanWithDefault",
template: "booleanWithDefault: ${true}",
},
],
{ mode: "json5" }
);
});
it("should include insert text for nested object properties", async () => {
await expectCompletion(
"{ object: { f| }",
[
{
type: "property",
detail: "string",
info: "an elegant string",
label: "foo",
template: "foo: '#{}'",
},
],
{ mode: "json5" }
);
});
it("should include insert text for nested oneOf object properties with a single quote", async () => {
await expectCompletion(
"{ oneOfObject: { '|' }",
[
{
type: "property",
detail: "string",
info: "",
label: "foo",
template: "'foo': '#{}'",
},
{
type: "property",
detail: "number",
info: "",
label: "bar",
template: "'bar': #{0}",
},
{
type: "property",
detail: "string",
info: "",
label: "apple",
template: "'apple': '#{}'",
},
{
type: "property",
detail: "number",
info: "",
label: "banana",
template: "'banana': #{0}",
},
],
{ mode: "json5" }
);
});
});
Loading