Skip to content

Commit

Permalink
bug for no value context
Browse files Browse the repository at this point in the history
  • Loading branch information
acao committed Nov 6, 2023
1 parent 56c88cd commit ebd59dd
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 323 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist
cjs
/public
coverage
cjs
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@types/json-schema": "^7.0.12",
"@types/node": "^20.4.2",
"json-schema": "^0.4.0",
"json-schema-library": "^8.0.0"
"json-schema-library": "^9.0.0"
},
"optionalDependencies": {
"@codemirror/lang-json": "^6.0.1",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 13 additions & 9 deletions src/__tests__/json-validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@ const getErrors = (jsonString: string, schema?: JSONSchema7) => {
const view = new EditorView({ doc: jsonString, extensions: [json()] });
return new JSONValidation(schema || testSchema).doValidation(view);
};

const common = {
severity: "error" as Diagnostic["severity"],
source: "json-schema",
};

const expectErrors = (
jsonString: string,
errors: [from: number, to: number, message: string][],
schema?: JSONSchema7
) => {
expect(getErrors(jsonString, schema)).toEqual(
const filteredErrors = getErrors(jsonString, schema).map(
({ renderMessage, ...error }) => error
);
expect(filteredErrors).toEqual(
errors.map(([from, to, message]) => ({ ...common, from, to, message }))
);
};

const common = {
severity: "error" as Diagnostic["severity"],
source: "json-schema",
};

describe("json-validation", () => {
it("should provide range for a value error", () => {
expectErrors('{"foo": 123}', [
Expand All @@ -34,7 +38,7 @@ describe("json-validation", () => {
});
it("should provide range for an unknown key error", () => {
expectErrors('{"foo": "example", "bar": 123}', [
[19, 24, "Additional property `bar` in `#` is not allowed"],
[19, 24, "Additional property `bar` is not allowed"],
]);
});
it("should not handle invalid json", () => {
Expand All @@ -46,7 +50,7 @@ describe("json-validation", () => {
"foo": "example",
"bar": "something else"
}`,
[[32, 37, "Additional property `bar` in `#` is not allowed"]]
[[32, 37, "Additional property `bar` is not allowed"]]
);
});
it("should provide formatted error message for oneOf fields with more than 2 items", () => {
Expand All @@ -65,7 +69,7 @@ describe("json-validation", () => {
"foo": "example",
"oneOfEg2": 123
}`,
[[44, 47, 'Expected one of `"string"` or `"array"`']],
[[44, 47, "Expected one of <code>string</code> or <code>array</code>"]],
testSchema2
);
});
Expand Down
17 changes: 7 additions & 10 deletions src/json-completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ import {
stripSurroundingQuotes,
getNodeAtPosition,
} from "./utils/node";
import { Draft07, JsonError } from "json-schema-library";
import { Draft07, JsonError, isJsonError } from "json-schema-library";
import { jsonPointerForPosition } from "./utils/jsonPointers";
import { TOKENS } from "./constants";
import getSchema from "./utils/schema-lib/getSchema";

function json5PropertyInsertSnippet(rawWord: string, value: string) {
if (rawWord.startsWith('"')) {
Expand Down Expand Up @@ -683,7 +682,10 @@ export class JSONCompletion {
): JSONSchema7Definition[] {
const draft = new Draft07(this.schema);
let pointer = jsonPointerForPosition(ctx.state, ctx.pos);
let subSchema = getSchema(draft, pointer);
let subSchema = draft.getSchema({ pointer });
if (isJsonError(subSchema)) {
subSchema = subSchema.data?.schema;
}
// if we don't have a schema for the current pointer, try the parent pointer
if (
!subSchema ||
Expand All @@ -692,7 +694,7 @@ export class JSONCompletion {
subSchema.type === "undefined"
) {
pointer = pointer.replace(/\/[^/]*$/, "/");
subSchema = getSchema(draft, pointer);
subSchema = draft.getSchema({ pointer });
}

debug.log("xxx", "pointer..", JSON.stringify(pointer));
Expand All @@ -703,8 +705,7 @@ export class JSONCompletion {
}
// const subSchema = new Draft07(this.schema).getSchema(pointer);
debug.log("xxx", "subSchema..", subSchema);

if (this.isJsonError(subSchema)) {
if (!subSchema) {
return [];
}

Expand All @@ -730,10 +731,6 @@ export class JSONCompletion {
return [subSchema as JSONSchema7];
}

isJsonError(d: JSONSchema7 | JsonError): d is JsonError {
return d.type === "error";
}

private expandSchemaProperty(
property: JSONSchema7Definition,
schema: JSONSchema7
Expand Down
54 changes: 43 additions & 11 deletions src/json-hover.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { type EditorView, Tooltip } from "@codemirror/view";
import { type Draft, Draft04, JsonSchema } from "json-schema-library";
import {
type Draft,
Draft04,
JsonSchema,
isJsonError,
} from "json-schema-library";
import type { JSONSchema7 } from "json-schema";

import { JSONMode, jsonPointerForPosition } from "./utils/jsonPointers";
import { joinWithOr } from "./utils/formatting";
import getSchema from "./utils/schema-lib/getSchema";
import { debug } from "./utils/debug";
import { Side } from "./types";
import { el } from "./utils/dom";
Expand Down Expand Up @@ -56,8 +60,11 @@ function formatComplexType(

export class JSONHover {
private schema: Draft;
public constructor(schema: JSONSchema7, private opts?: HoverOptions) {
this.schema = new Draft04(schema);
public constructor(
private _schema: JSONSchema7,
private opts?: HoverOptions
) {
this.schema = new Draft04(_schema);
this.opts = {
parser: JSON.parse,
...this.opts,
Expand All @@ -80,12 +87,19 @@ export class JSONHover {
return null;
}
// if the data is valid, we can infer a type for complex types
let subSchema = getSchema(this.schema, pointer, data);
if (subSchema.type === "error" && data !== undefined) {
// if the data is invalid, we won't get the type - try again without the data
subSchema = getSchema(this.schema, pointer, undefined);
if (subSchema.type === "error") {
return { pointer };
let subSchema = this.schema.getSchema({
pointer,
data,
schema: this._schema,
withSchemaWarning: true,
});
if (isJsonError(subSchema)) {
console.log("subschema", subSchema.data);

if (subSchema?.data.schema["$ref"]) {
subSchema = this.schema.resolveRef(subSchema);
} else {
subSchema = subSchema?.data.schema;
}
}

Expand All @@ -101,7 +115,15 @@ export class JSONHover {
text: message,
}),
el("div", { class: "cm6-json-schema-hover--code-wrapper" }, [
el("code", { class: "cm6-json-schema-hover--code", text: typeInfo }),
typeInfo.includes("<code>")
? el("div", {
class: "cm6-json-schema-hover--code",
inner: typeInfo,
})
: el("code", {
class: "cm6-json-schema-hover--code",
text: typeInfo,
}),
]),
]);
}
Expand All @@ -117,6 +139,7 @@ export class JSONHover {
let message = null;

const { schema } = data;
console.log(schema, data);
if (schema.oneOf) {
typeInfo = formatComplexType(schema, "oneOf", draft);
}
Expand All @@ -131,6 +154,15 @@ export class JSONHover {
? joinWithOr(schema.type)
: schema.type;
}
if (schema.enum) {
typeInfo = `<code>enum</code>: ${joinWithOr(schema.enum)}`;
}
if (schema.format) {
typeInfo += ` <code>format</code>: ${schema.format}`;
}
if (schema.pattern) {
typeInfo += ` <code>pattern</code>: ${schema.pattern}`;
}
if (schema.description) {
message = schema.description;
}
Expand Down
26 changes: 19 additions & 7 deletions src/json-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { joinWithOr } from "./utils/formatting";
import { JSONPointerData } from "./types";
import { parseJSONDocumentState } from "./utils/parseJSONDocument";
import { RequiredPick } from "./types";
import { el } from "./utils/dom";

// return an object path that matches with the json-source-map pointer
const getErrorPath = (error: JsonError): string => {
Expand Down Expand Up @@ -59,26 +60,33 @@ export class JSONValidation {
this.schema = new Draft04(schema);
}
private get schemaTitle() {
return this.schema.getSchema().title ?? "json-schema";
return this.schema.getSchema()?.title ?? "json-schema";
}

// rewrite the error message to be more human readable
private rewriteError = (error: JsonError): string => {
if (error.code === "one-of-error") {
console.log("raw", error?.data?.received);
return `Expected one of ${joinWithOr(
error?.data?.errors,
(data) => data.data.expected
)}`;
}
if (error.code === "type-error") {
return `Expected \`${
console.log("raw", error?.data?.received);
return `Expected <code>${
error?.data?.expected && Array.isArray(error?.data?.expected)
? joinWithOr(error?.data?.expected)
: error?.data?.expected
}\` but received \`${error?.data?.received}\``;
}</code> but received <code>${error?.data?.received}</code>`;
}
const message = error.message.replaceAll("#/", "").replaceAll("/", ".");

const message = error.message
// don't mention root object
.replaceAll("in `#` ", "")
.replaceAll("/", ".")
.replaceAll("#.", "")
// replace backticks with <code> tags
.replaceAll(/`([^`]*)`/gm, "<code>$1</code>");
return message;
};

Expand All @@ -100,6 +108,7 @@ export class JSONValidation {
if (!errors.length) return [];
// reduce() because we want to filter out errors that don't have a pointer
return errors.reduce((acc, error) => {
console.log(this.rewriteError(error));
const errorPath = getErrorPath(error);
const pointer = json.pointers.get(errorPath) as JSONPointerData;
if (pointer) {
Expand All @@ -108,9 +117,12 @@ export class JSONValidation {
acc.push({
from: isPropertyError ? pointer.keyFrom : pointer.valueFrom,
to: isPropertyError ? pointer.keyTo : pointer.valueTo,
// TODO: create a domnode and replace `` with <code></code>
// renderMessage: () => error.message,
message: this.rewriteError(error),
renderMessage: () => {
const dom = el("div", {});
dom.innerHTML = this.rewriteError(error);
return dom;
},
severity: "error",
source: this.schemaTitle,
});
Expand Down
6 changes: 5 additions & 1 deletion src/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// return e;
// }

type Attributes = "class" | "text" | "id" | "role" | "aria-label";
type Attributes = "class" | "text" | "id" | "role" | "aria-label" | "inner";

export function el(
tagName: string,
Expand All @@ -19,6 +19,10 @@ export function el(
e.innerText = v;
return;
}
if (k === "inner") {
e.innerHTML = v;
return;
}
e.setAttribute(k, v);
});
children.forEach((c) => e.appendChild(c));
Expand Down
2 changes: 1 addition & 1 deletion src/utils/formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
export const joinWithOr = (arr: string[], getPath?: (err: any) => any) => {
const needsComma = arr.length > 2;
let data = arr.map((err: any, i: number) => {
const result = `\`` + (getPath ? JSON.stringify(getPath(err)) : err) + `\``;
const result = `<code>` + (getPath ? getPath(err) : err) + `</code>`;
if (i === arr.length - 1) return "or " + result;
return result;
});
Expand Down
3 changes: 0 additions & 3 deletions src/utils/schema-lib/README.md

This file was deleted.

Loading

0 comments on commit ebd59dd

Please sign in to comment.