skir-codemirror-plugin 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -6
- package/dist/codemirror/create_editor_state.d.ts +4 -2
- package/dist/codemirror/create_editor_state.d.ts.map +1 -1
- package/dist/codemirror/create_editor_state.js +178 -31
- package/dist/codemirror/create_editor_state.js.map +1 -1
- package/dist/codemirror/json_linter.d.ts.map +1 -1
- package/dist/codemirror/json_linter.js +67 -3
- package/dist/codemirror/json_linter.js.map +1 -1
- package/dist/codemirror/json_state.d.ts +5 -1
- package/dist/codemirror/json_state.d.ts.map +1 -1
- package/dist/codemirror/json_state.js +26 -1
- package/dist/codemirror/json_state.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/json/json_parser.js +35 -1
- package/dist/json/json_parser.js.map +1 -1
- package/dist/json/json_parser.test.js +48 -0
- package/dist/json/json_parser.test.js.map +1 -1
- package/dist/json/schema_validator.d.ts.map +1 -1
- package/dist/json/schema_validator.js +16 -11
- package/dist/json/schema_validator.js.map +1 -1
- package/dist/json/schema_validator.test.js +71 -0
- package/dist/json/schema_validator.test.js.map +1 -1
- package/dist/json/to_json.d.ts +1 -1
- package/dist/json/to_json.d.ts.map +1 -1
- package/dist/json/to_json.js +3 -3
- package/dist/json/to_json.js.map +1 -1
- package/dist/json/types.d.ts +4 -0
- package/dist/json/types.d.ts.map +1 -1
- package/dist/json/types.js.map +1 -1
- package/package.json +2 -3
- package/src/codemirror/create_editor_state.ts +272 -31
- package/src/codemirror/json_linter.ts +89 -4
- package/src/codemirror/json_state.ts +44 -1
- package/src/index.ts +1 -0
- package/src/json/json_parser.test.ts +51 -0
- package/src/json/json_parser.ts +37 -1
- package/src/json/schema_validator.test.ts +75 -0
- package/src/json/schema_validator.ts +20 -10
- package/src/json/to_json.ts +3 -3
- package/src/json/types.ts +7 -0
package/dist/json/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/json/types.ts"],"names":[],"mappings":"","sourcesContent":["// To use instead of `any`\nexport type Json =\n | null\n | boolean\n | number\n | string\n | readonly Json[]\n | Readonly<{ [name: string]: Json }>;\n\n// -----------------------------------------------------------------------------\n// ERRORS\n// -----------------------------------------------------------------------------\n\nexport interface JsonError {\n readonly kind: \"error\";\n readonly segment: Segment;\n readonly message: string;\n}\n\n// -----------------------------------------------------------------------------\n// PARSING\n// -----------------------------------------------------------------------------\n\nexport interface JsonParseResult {\n /// If `undefined`, then `errors` is guaranteed not to be empty.\n readonly value: JsonValue | undefined;\n readonly errors: readonly JsonError[];\n /// Set of edits to apply to the original JSON code to fix formatting and\n // comma errors (no comma between consecutive values, trailing comma).\n readonly edits: readonly JsonEdit[];\n}\n\nexport type JsonValue = JsonArray | JsonObject | JsonLiteral;\n\nexport interface JsonArray {\n readonly kind: \"array\";\n /// Position of the '[' token.\n readonly firstToken: Segment;\n /// From '[' to ']' included.\n readonly segment: Segment;\n readonly values: JsonValue[];\n expectedType?: TypeSignature;\n}\n\nexport interface JsonKey {\n readonly keySegment: Segment;\n readonly key: string;\n}\n\nexport interface JsonKeyValue {\n readonly keySegment: Segment;\n readonly key: string;\n readonly value: JsonValue;\n expectedType?: TypeSignature;\n}\n\nexport interface JsonObject {\n readonly kind: \"object\";\n /// Position of the '{' token.\n readonly firstToken: Segment;\n /// From '{' to '}' included.\n readonly segment: Segment;\n readonly keyValues: { [key: string]: JsonKeyValue };\n /// Includes \"broken\" keys which produced a parsing error.\n readonly allKeys: readonly JsonKey[];\n expectedType?: TypeSignature;\n}\n\nexport interface JsonLiteral {\n readonly kind: \"literal\";\n /// Position of the first and only token. Same as `segment`.\n readonly firstToken: Segment;\n readonly segment: Segment;\n readonly jsonCode: string;\n readonly type: \"boolean\" | \"null\" | \"number\" | \"string\";\n expectedType?: TypeSignature;\n}\n\nexport interface Segment {\n readonly start: number;\n readonly end: number;\n}\n\n/// Path to a sub-value within a valid Skir value.\nexport type Path =\n | {\n readonly kind: \"root\";\n }\n | {\n readonly kind: \"field-value\";\n /// Name of the field of the struct\n readonly fieldName: string;\n /// Path to the struct value\n readonly structPath: Path;\n }\n | {\n readonly kind: \"variant-value\";\n /// Name of the wrapper variant of the enum\n readonly variantName: string;\n /// Path to the enum value\n readonly enumPath: Path;\n }\n | {\n readonly kind: \"array-item\";\n readonly index: number;\n readonly key: string | null;\n readonly arrayPath: Path;\n };\n\nexport interface JsonValueContext {\n readonly value: JsonValue;\n readonly path: Path;\n}\n\nexport interface JsonEdit {\n readonly segment: Segment;\n readonly replacement: string;\n}\n\n// -----------------------------------------------------------------------------\n// SCHEMA\n// -----------------------------------------------------------------------------\n\n/** JSON representation of a `TypeDescriptor`. */\nexport type TypeDefinition = {\n readonly type: TypeSignature;\n readonly records: readonly RecordDefinition[];\n};\n\n/** A type in the JSON representation of a `TypeDescriptor`. */\nexport type TypeSignature =\n | {\n kind: \"optional\";\n value: TypeSignature;\n }\n | ArrayTypeSignature\n | RecordTypeSignature\n | {\n kind: \"primitive\";\n value: PrimitiveType;\n };\n\nexport interface ArrayTypeSignature {\n readonly kind: \"array\";\n readonly value: {\n readonly item: TypeSignature;\n readonly key_extractor?: string;\n };\n}\n\nexport interface RecordTypeSignature {\n readonly kind: \"record\";\n readonly value: string;\n}\n\nexport type PrimitiveType =\n | \"bool\"\n | \"int32\"\n | \"int64\"\n | \"hash64\"\n | \"float32\"\n | \"float64\"\n | \"timestamp\"\n | \"string\"\n | \"bytes\";\n\nexport type RecordDefinition = StructDefinition | EnumDefinition;\n\n/** Definition of a struct in the JSON representation of a `TypeDescriptor`. */\nexport type StructDefinition = {\n readonly kind: \"struct\";\n readonly id: string;\n readonly doc?: string;\n readonly fields: readonly FieldDefinition[];\n readonly removed_numbers?: readonly number[];\n};\n\n/**\n * Definition of a struct field in the JSON representation of a\n * `TypeDescriptor`.\n */\nexport type FieldDefinition = {\n readonly name: string;\n readonly type: TypeSignature;\n readonly number: number;\n readonly doc?: string;\n};\n\n/** Definition of an enum in the JSON representation of a `TypeDescriptor`. */\nexport type EnumDefinition = {\n readonly kind: \"enum\";\n readonly id: string;\n readonly doc?: string;\n readonly variants: readonly VariantDefinition[];\n readonly removed_numbers?: readonly number[];\n};\n\n/**\n * Definition of an enum variant in the JSON representation of a\n * `TypeDescriptor`.\n */\nexport type VariantDefinition = {\n readonly name: string;\n readonly type?: TypeSignature;\n readonly number: number;\n readonly doc?: string;\n};\n\n// -----------------------------------------------------------------------------\n// SCHEMA VALIDATION\n// -----------------------------------------------------------------------------\n\nexport interface ValidationResult {\n readonly errors: JsonError[];\n readonly hints: Hint[];\n readonly rootTypeHint: TypeHint | undefined;\n readonly pathToTypeHint: ReadonlyMap<Path, TypeHint>;\n}\n\nexport interface TypeHint {\n readonly segment: Segment;\n readonly message: string | readonly string[];\n readonly valueContext: JsonValueContext;\n /// In order. All are included in 'valueContext.value.segment'.\n readonly childHints: readonly TypeHint[];\n}\n\nexport type Hint =\n | {\n readonly segment: Segment;\n readonly message: string;\n readonly valueContext?: undefined;\n }\n | TypeHint;\n\nexport interface MutableTypeHint extends TypeHint {\n readonly childHints: TypeHint[];\n}\n\n// -----------------------------------------------------------------------------\n// METHOD LIST\n// -----------------------------------------------------------------------------\n\nexport interface MethodList {\n readonly methods: readonly Method[];\n}\n\nexport interface Method {\n readonly method: string;\n readonly number: number | string;\n readonly request: TypeDefinition;\n readonly response: TypeDefinition;\n readonly doc?: string;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/json/types.ts"],"names":[],"mappings":"","sourcesContent":["// To use instead of `any`\nexport type Json =\n | null\n | boolean\n | number\n | string\n | readonly Json[]\n | Readonly<{ [name: string]: Json }>;\n\n// -----------------------------------------------------------------------------\n// ERRORS\n// -----------------------------------------------------------------------------\n\nexport interface JsonError {\n readonly kind: \"error\";\n readonly segment: Segment;\n readonly message: string;\n}\n\n// -----------------------------------------------------------------------------\n// PARSING\n// -----------------------------------------------------------------------------\n\nexport interface JsonParseResult {\n /// If `undefined`, then `errors` is guaranteed not to be empty.\n readonly value: JsonValue | undefined;\n readonly errors: readonly JsonError[];\n /// Set of edits to apply to the original JSON code to fix formatting and\n // comma errors (no comma between consecutive values, trailing comma).\n readonly edits: readonly JsonEdit[];\n}\n\nexport type JsonValue = JsonArray | JsonObject | JsonLiteral;\n\nexport interface JsonArray {\n readonly kind: \"array\";\n /// Position of the '[' token.\n readonly firstToken: Segment;\n /// From '[' to ']' included.\n readonly segment: Segment;\n readonly values: JsonValue[];\n readonly indent: number;\n expectedType?: TypeSignature;\n}\n\nexport interface JsonKey {\n readonly keySegment: Segment;\n readonly key: string;\n}\n\nexport interface JsonKeyValue {\n readonly keySegment: Segment;\n readonly key: string;\n readonly value: JsonValue;\n expectedType?: TypeSignature;\n}\n\nexport interface JsonObject {\n readonly kind: \"object\";\n /// Position of the '{' token.\n readonly firstToken: Segment;\n /// From '{' to '}' included.\n readonly segment: Segment;\n readonly keyValues: { [key: string]: JsonKeyValue };\n /// Includes \"broken\" keys which produced a parsing error.\n readonly allKeys: readonly JsonKey[];\n readonly indent: number;\n expectedType?: TypeSignature;\n}\n\nexport interface JsonLiteral {\n readonly kind: \"literal\";\n /// Position of the first and only token. Same as `segment`.\n readonly firstToken: Segment;\n readonly segment: Segment;\n readonly jsonCode: string;\n readonly type: \"boolean\" | \"null\" | \"number\" | \"string\";\n readonly indent: number;\n expectedType?: TypeSignature;\n}\n\nexport interface Segment {\n readonly start: number;\n readonly end: number;\n}\n\n/// Path to a sub-value within a valid Skir value.\nexport type Path =\n | {\n readonly kind: \"root\";\n }\n | {\n readonly kind: \"field-value\";\n /// Name of the field of the struct\n readonly fieldName: string;\n /// Path to the struct value\n readonly structPath: Path;\n }\n | {\n readonly kind: \"variant-value\";\n /// Name of the wrapper variant of the enum\n readonly variantName: string;\n /// Path to the enum value\n readonly enumPath: Path;\n }\n | {\n readonly kind: \"array-item\";\n readonly index: number;\n readonly key: string | null;\n readonly arrayPath: Path;\n };\n\nexport interface JsonValueContext {\n readonly value: JsonValue;\n readonly path: Path;\n}\n\nexport interface JsonEdit {\n readonly segment: Segment;\n readonly replacement: string;\n}\n\n// -----------------------------------------------------------------------------\n// SCHEMA\n// -----------------------------------------------------------------------------\n\n/** JSON representation of a `TypeDescriptor`. */\nexport type TypeDefinition = {\n readonly type: TypeSignature;\n readonly records: readonly RecordDefinition[];\n};\n\n/** A type in the JSON representation of a `TypeDescriptor`. */\nexport type TypeSignature =\n | {\n kind: \"optional\";\n value: TypeSignature;\n }\n | ArrayTypeSignature\n | RecordTypeSignature\n | {\n kind: \"primitive\";\n value: PrimitiveType;\n };\n\nexport interface ArrayTypeSignature {\n readonly kind: \"array\";\n readonly value: {\n readonly item: TypeSignature;\n readonly key_extractor?: string;\n };\n}\n\nexport interface RecordTypeSignature {\n readonly kind: \"record\";\n readonly value: string;\n}\n\nexport type PrimitiveType =\n | \"bool\"\n | \"int32\"\n | \"int64\"\n | \"hash64\"\n | \"float32\"\n | \"float64\"\n | \"timestamp\"\n | \"string\"\n | \"bytes\";\n\nexport type RecordDefinition = StructDefinition | EnumDefinition;\n\n/** Definition of a struct in the JSON representation of a `TypeDescriptor`. */\nexport type StructDefinition = {\n readonly kind: \"struct\";\n readonly id: string;\n readonly doc?: string;\n readonly fields: readonly FieldDefinition[];\n readonly removed_numbers?: readonly number[];\n};\n\n/**\n * Definition of a struct field in the JSON representation of a\n * `TypeDescriptor`.\n */\nexport type FieldDefinition = {\n readonly name: string;\n readonly type: TypeSignature;\n readonly number: number;\n readonly doc?: string;\n};\n\n/** Definition of an enum in the JSON representation of a `TypeDescriptor`. */\nexport type EnumDefinition = {\n readonly kind: \"enum\";\n readonly id: string;\n readonly doc?: string;\n readonly variants: readonly VariantDefinition[];\n readonly removed_numbers?: readonly number[];\n};\n\n/**\n * Definition of an enum variant in the JSON representation of a\n * `TypeDescriptor`.\n */\nexport type VariantDefinition = {\n readonly name: string;\n readonly type?: TypeSignature;\n readonly number: number;\n readonly doc?: string;\n};\n\n// -----------------------------------------------------------------------------\n// SCHEMA VALIDATION\n// -----------------------------------------------------------------------------\n\nexport interface ValidationResult {\n readonly errors: JsonError[];\n readonly hints: Hint[];\n readonly rootTypeHint: TypeHint | undefined;\n readonly pathToTypeHint: ReadonlyMap<Path, TypeHint>;\n}\n\nexport interface TypeHint {\n readonly segment: Segment;\n readonly message: string | readonly string[];\n readonly valueContext: JsonValueContext;\n /// In order. All are included in 'valueContext.value.segment'.\n readonly childHints: readonly TypeHint[];\n /// Set if the expected type is an enum and the value is a literal string.\n /// In that case, the editor can show a dropdown with the possible variants\n /// of the enum.\n readonly enumDefinition?: EnumDefinition;\n}\n\nexport type Hint =\n | {\n readonly segment: Segment;\n readonly message: string;\n readonly valueContext?: undefined;\n }\n | TypeHint;\n\nexport interface MutableTypeHint extends TypeHint {\n readonly childHints: TypeHint[];\n}\n\n// -----------------------------------------------------------------------------\n// METHOD LIST\n// -----------------------------------------------------------------------------\n\nexport interface MethodList {\n readonly methods: readonly Method[];\n}\n\nexport interface Method {\n readonly method: string;\n readonly number: number | string;\n readonly request: TypeDefinition;\n readonly response: TypeDefinition;\n readonly doc?: string;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skir-codemirror-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://github.com/gepheum/skir-codemirror-plugin#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -46,8 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@codemirror/lang-json": "^6.0.2",
|
|
49
|
-
"@uiw/codemirror-
|
|
50
|
-
"@uiw/codemirror-theme-tokyo-night-day": "^4.25.9",
|
|
49
|
+
"@uiw/codemirror-themes-all": "^4.25.9",
|
|
51
50
|
"codemirror": "^6.0.2",
|
|
52
51
|
"skir-client": "^1.0.11"
|
|
53
52
|
},
|
|
@@ -3,8 +3,7 @@ import { json as jsonExtension } from "@codemirror/lang-json";
|
|
|
3
3
|
import { linter, lintGutter } from "@codemirror/lint";
|
|
4
4
|
import { EditorState, Extension } from "@codemirror/state";
|
|
5
5
|
import { EditorView } from "@codemirror/view";
|
|
6
|
-
import
|
|
7
|
-
import { tokyoNightDay } from "@uiw/codemirror-theme-tokyo-night-day";
|
|
6
|
+
import * as codeMirrorThemes from "@uiw/codemirror-themes-all";
|
|
8
7
|
import { basicSetup } from "codemirror";
|
|
9
8
|
import { makeJsonTemplate } from "../json/to_json";
|
|
10
9
|
import type { Json, RecordDefinition, TypeDefinition } from "../json/types";
|
|
@@ -18,14 +17,279 @@ export type CreateEditorStateParams = {
|
|
|
18
17
|
schema: TypeDefinition;
|
|
19
18
|
readOnly?: true;
|
|
20
19
|
json?: Json;
|
|
21
|
-
theme?:
|
|
20
|
+
theme?: BuiltinThemeName | CustomTheme;
|
|
21
|
+
otherExtension?: Extension;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
export type BuiltinThemeName =
|
|
25
|
+
| "abcdef"
|
|
26
|
+
| "abyss"
|
|
27
|
+
| "androidstudio"
|
|
28
|
+
| "andromeda"
|
|
29
|
+
| "atomone"
|
|
30
|
+
| "aura"
|
|
31
|
+
| "basic-dark"
|
|
32
|
+
| "basic-light"
|
|
33
|
+
| "bbedit"
|
|
34
|
+
| "bespin"
|
|
35
|
+
| "console-dark"
|
|
36
|
+
| "console-light"
|
|
37
|
+
| "copilot"
|
|
38
|
+
| "darcula"
|
|
39
|
+
| "dracula"
|
|
40
|
+
| "duotone-dark"
|
|
41
|
+
| "duotone-light"
|
|
42
|
+
| "eclipse"
|
|
43
|
+
| "github-dark"
|
|
44
|
+
| "github-light"
|
|
45
|
+
| "gruvbox-dark"
|
|
46
|
+
| "gruvbox-light"
|
|
47
|
+
| "kimbie"
|
|
48
|
+
| "material"
|
|
49
|
+
| "material-dark"
|
|
50
|
+
| "material-light"
|
|
51
|
+
| "monokai"
|
|
52
|
+
| "monokai-dimmed"
|
|
53
|
+
| "noctis-lilac"
|
|
54
|
+
| "nord"
|
|
55
|
+
| "okaidia"
|
|
56
|
+
| "quietlight"
|
|
57
|
+
| "red"
|
|
58
|
+
| "solarized-dark"
|
|
59
|
+
| "solarized-light"
|
|
60
|
+
| "sublime"
|
|
61
|
+
| "tokyo-night"
|
|
62
|
+
| "tokyo-night-day"
|
|
63
|
+
| "tokyo-night-storm"
|
|
64
|
+
| "tomorrow-night-blue"
|
|
65
|
+
| "vscode-dark"
|
|
66
|
+
| "vscode-light"
|
|
67
|
+
| "white"
|
|
68
|
+
| "white-dark"
|
|
69
|
+
| "white-light"
|
|
70
|
+
| "xcode-dark"
|
|
71
|
+
| "xcode-light";
|
|
72
|
+
|
|
73
|
+
const WHITE_THEME_COLORS: Omit<CustomTheme, "themeExtension"> = {
|
|
74
|
+
backgroundColor: "#fffdf7",
|
|
75
|
+
lighterBgColor: "#f3eee1",
|
|
76
|
+
borderColor: "#d4d0c4",
|
|
77
|
+
foregroundColor: "#111111",
|
|
78
|
+
accentColor: "#111111",
|
|
79
|
+
errorColor: "#232323",
|
|
80
|
+
selectionColor: "#d9d4c7",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const TOKYO_NIGHT_THEME_COLORS: Omit<CustomTheme, "themeExtension"> = {
|
|
84
|
+
backgroundColor: "#1a1b26",
|
|
85
|
+
lighterBgColor: "#1f2335",
|
|
86
|
+
borderColor: "#414868",
|
|
87
|
+
foregroundColor: "#c0caf5",
|
|
88
|
+
accentColor: "#7aa2f7",
|
|
89
|
+
errorColor: "#f7768e",
|
|
90
|
+
selectionColor: "#515c7e40",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const TOKYO_NIGHT_DAY_THEME_COLORS: Omit<CustomTheme, "themeExtension"> = {
|
|
94
|
+
backgroundColor: "#d5d6db",
|
|
95
|
+
lighterBgColor: "#e1e2e7",
|
|
96
|
+
borderColor: "#adb0bb",
|
|
97
|
+
foregroundColor: "#3760bf",
|
|
98
|
+
accentColor: "#2e7de9",
|
|
99
|
+
errorColor: "#f52a65",
|
|
100
|
+
selectionColor: "#3760bf33",
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type ThemeDefaults = Partial<{
|
|
104
|
+
background: string;
|
|
105
|
+
foreground: string;
|
|
106
|
+
caret: string;
|
|
107
|
+
selection: string;
|
|
108
|
+
selectionMatch: string;
|
|
109
|
+
gutterBackground: string;
|
|
110
|
+
gutterForeground: string;
|
|
111
|
+
gutterBorder: string;
|
|
112
|
+
lineHighlight: string;
|
|
113
|
+
}>;
|
|
114
|
+
|
|
115
|
+
const BUILTIN_THEME_EXTENSIONS: Record<BuiltinThemeName, Extension> = {
|
|
116
|
+
abcdef: codeMirrorThemes.abcdef,
|
|
117
|
+
abyss: codeMirrorThemes.abyss,
|
|
118
|
+
androidstudio: codeMirrorThemes.androidstudio,
|
|
119
|
+
andromeda: codeMirrorThemes.andromeda,
|
|
120
|
+
atomone: codeMirrorThemes.atomone,
|
|
121
|
+
aura: codeMirrorThemes.aura,
|
|
122
|
+
"basic-dark": codeMirrorThemes.basicDark,
|
|
123
|
+
"basic-light": codeMirrorThemes.basicLight,
|
|
124
|
+
bbedit: codeMirrorThemes.bbedit,
|
|
125
|
+
bespin: codeMirrorThemes.bespin,
|
|
126
|
+
"console-dark": codeMirrorThemes.consoleDark,
|
|
127
|
+
"console-light": codeMirrorThemes.consoleLight,
|
|
128
|
+
copilot: codeMirrorThemes.copilot,
|
|
129
|
+
darcula: codeMirrorThemes.darcula,
|
|
130
|
+
dracula: codeMirrorThemes.dracula,
|
|
131
|
+
"duotone-dark": codeMirrorThemes.duotoneDark,
|
|
132
|
+
"duotone-light": codeMirrorThemes.duotoneLight,
|
|
133
|
+
eclipse: codeMirrorThemes.eclipse,
|
|
134
|
+
"github-dark": codeMirrorThemes.githubDark,
|
|
135
|
+
"github-light": codeMirrorThemes.githubLight,
|
|
136
|
+
"gruvbox-dark": codeMirrorThemes.gruvboxDark,
|
|
137
|
+
"gruvbox-light": codeMirrorThemes.gruvboxLight,
|
|
138
|
+
kimbie: codeMirrorThemes.kimbie,
|
|
139
|
+
material: codeMirrorThemes.material,
|
|
140
|
+
"material-dark": codeMirrorThemes.materialDark,
|
|
141
|
+
"material-light": codeMirrorThemes.materialLight,
|
|
142
|
+
monokai: codeMirrorThemes.monokai,
|
|
143
|
+
"monokai-dimmed": codeMirrorThemes.monokaiDimmed,
|
|
144
|
+
"noctis-lilac": codeMirrorThemes.noctisLilac,
|
|
145
|
+
nord: codeMirrorThemes.nord,
|
|
146
|
+
okaidia: codeMirrorThemes.okaidia,
|
|
147
|
+
quietlight: codeMirrorThemes.quietlight,
|
|
148
|
+
red: codeMirrorThemes.red,
|
|
149
|
+
"solarized-dark": codeMirrorThemes.solarizedDark,
|
|
150
|
+
"solarized-light": codeMirrorThemes.solarizedLight,
|
|
151
|
+
sublime: codeMirrorThemes.sublime,
|
|
152
|
+
"tokyo-night": codeMirrorThemes.tokyoNight,
|
|
153
|
+
"tokyo-night-day": codeMirrorThemes.tokyoNightDay,
|
|
154
|
+
"tokyo-night-storm": codeMirrorThemes.tokyoNightStorm,
|
|
155
|
+
"tomorrow-night-blue": codeMirrorThemes.tomorrowNightBlue,
|
|
156
|
+
"vscode-dark": codeMirrorThemes.vscodeDark,
|
|
157
|
+
"vscode-light": codeMirrorThemes.vscodeLight,
|
|
158
|
+
white: codeMirrorThemes.whiteLight,
|
|
159
|
+
"white-dark": codeMirrorThemes.whiteDark,
|
|
160
|
+
"white-light": codeMirrorThemes.whiteLight,
|
|
161
|
+
"xcode-dark": codeMirrorThemes.xcodeDark,
|
|
162
|
+
"xcode-light": codeMirrorThemes.xcodeLight,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const BUILTIN_THEME_DEFAULTS: Record<BuiltinThemeName, ThemeDefaults> = {
|
|
166
|
+
abcdef: codeMirrorThemes.defaultSettingsAbcdef,
|
|
167
|
+
abyss: codeMirrorThemes.defaultSettingsAbyss,
|
|
168
|
+
androidstudio: codeMirrorThemes.defaultSettingsAndroidstudio,
|
|
169
|
+
andromeda: codeMirrorThemes.defaultSettingsAndromeda,
|
|
170
|
+
atomone: codeMirrorThemes.defaultSettingsAtomone,
|
|
171
|
+
aura: codeMirrorThemes.defaultSettingsAura,
|
|
172
|
+
"basic-dark": codeMirrorThemes.defaultSettingsBasicDark,
|
|
173
|
+
"basic-light": codeMirrorThemes.defaultSettingsBasicLight,
|
|
174
|
+
bbedit: codeMirrorThemes.defaultSettingsBbedit,
|
|
175
|
+
bespin: codeMirrorThemes.defaultSettingsBespin,
|
|
176
|
+
"console-dark": codeMirrorThemes.defaultSettingsConsoleDark,
|
|
177
|
+
"console-light": codeMirrorThemes.defaultSettingsConsoleLight,
|
|
178
|
+
copilot: codeMirrorThemes.defaultSettingsCopilot,
|
|
179
|
+
darcula: codeMirrorThemes.defaultSettingsDarcula,
|
|
180
|
+
dracula: codeMirrorThemes.defaultSettingsDracula,
|
|
181
|
+
"duotone-dark": codeMirrorThemes.defaultSettingsDuotoneDark,
|
|
182
|
+
"duotone-light": codeMirrorThemes.defaultSettingsDuotoneLight,
|
|
183
|
+
eclipse: codeMirrorThemes.defaultSettingsEclipse,
|
|
184
|
+
"github-dark": codeMirrorThemes.defaultSettingsGithubDark,
|
|
185
|
+
"github-light": codeMirrorThemes.defaultSettingsGithubLight,
|
|
186
|
+
"gruvbox-dark": codeMirrorThemes.defaultSettingsGruvboxDark,
|
|
187
|
+
"gruvbox-light": codeMirrorThemes.defaultSettingsGruvboxLight,
|
|
188
|
+
kimbie: codeMirrorThemes.defaultSettingsKimbie,
|
|
189
|
+
material: codeMirrorThemes.defaultSettingsMaterial,
|
|
190
|
+
"material-dark": codeMirrorThemes.defaultSettingsMaterialDark,
|
|
191
|
+
"material-light": codeMirrorThemes.defaultSettingsMaterialLight,
|
|
192
|
+
monokai: codeMirrorThemes.defaultSettingsMonokai,
|
|
193
|
+
"monokai-dimmed": codeMirrorThemes.defaultSettingsMonokaiDimmed,
|
|
194
|
+
"noctis-lilac": codeMirrorThemes.defaultSettingsNoctisLilac,
|
|
195
|
+
nord: codeMirrorThemes.defaultSettingsNord,
|
|
196
|
+
okaidia: codeMirrorThemes.defaultSettingsOkaidia,
|
|
197
|
+
quietlight: codeMirrorThemes.defaultSettingsQuietlight,
|
|
198
|
+
red: codeMirrorThemes.defaultSettingsRed,
|
|
199
|
+
"solarized-dark": codeMirrorThemes.defaultSettingsSolarizedDark,
|
|
200
|
+
"solarized-light": codeMirrorThemes.defaultSettingsSolarizedLight,
|
|
201
|
+
sublime: codeMirrorThemes.defaultSettingsSublime,
|
|
202
|
+
"tokyo-night": codeMirrorThemes.defaultSettingsTokyoNight,
|
|
203
|
+
"tokyo-night-day": codeMirrorThemes.defaultSettingsTokyoNightDay,
|
|
204
|
+
"tokyo-night-storm": codeMirrorThemes.defaultSettingsTokyoNightStorm,
|
|
205
|
+
"tomorrow-night-blue": codeMirrorThemes.defaultSettingsTomorrowNightBlue,
|
|
206
|
+
"vscode-dark": codeMirrorThemes.defaultSettingsVscodeDark,
|
|
207
|
+
"vscode-light": codeMirrorThemes.defaultSettingsVscodeLight,
|
|
208
|
+
white: codeMirrorThemes.defaultSettingsWhiteLight,
|
|
209
|
+
"white-dark": codeMirrorThemes.defaultSettingsWhiteDark,
|
|
210
|
+
"white-light": codeMirrorThemes.defaultSettingsWhiteLight,
|
|
211
|
+
"xcode-dark": codeMirrorThemes.defaultSettingsXcodeDark,
|
|
212
|
+
"xcode-light": codeMirrorThemes.defaultSettingsXcodeLight,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
function pickColor(
|
|
216
|
+
...candidates: Array<string | undefined>
|
|
217
|
+
): string | undefined {
|
|
218
|
+
for (const candidate of candidates) {
|
|
219
|
+
if (candidate && candidate !== "transparent") {
|
|
220
|
+
return candidate;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function themeFromDefaults(
|
|
227
|
+
defaults: ThemeDefaults,
|
|
228
|
+
themeExtension: Extension,
|
|
229
|
+
): CustomTheme {
|
|
230
|
+
const background = pickColor(defaults.background, defaults.gutterBackground);
|
|
231
|
+
const foreground = pickColor(
|
|
232
|
+
defaults.caret,
|
|
233
|
+
defaults.foreground,
|
|
234
|
+
defaults.gutterForeground,
|
|
235
|
+
);
|
|
236
|
+
const selection = pickColor(
|
|
237
|
+
defaults.selection,
|
|
238
|
+
defaults.selectionMatch,
|
|
239
|
+
defaults.lineHighlight,
|
|
240
|
+
);
|
|
241
|
+
return {
|
|
242
|
+
backgroundColor: background ?? "#1a1b26",
|
|
243
|
+
lighterBgColor:
|
|
244
|
+
pickColor(
|
|
245
|
+
defaults.gutterBackground,
|
|
246
|
+
defaults.lineHighlight,
|
|
247
|
+
background,
|
|
248
|
+
) ?? "#1f2335",
|
|
249
|
+
borderColor:
|
|
250
|
+
pickColor(
|
|
251
|
+
defaults.gutterBorder,
|
|
252
|
+
defaults.selectionMatch,
|
|
253
|
+
defaults.selection,
|
|
254
|
+
defaults.foreground,
|
|
255
|
+
) ?? "#414868",
|
|
256
|
+
foregroundColor: foreground ?? "#c0caf5",
|
|
257
|
+
accentColor: pickColor(defaults.foreground, defaults.caret) ?? "#7aa2f7",
|
|
258
|
+
errorColor: pickColor(defaults.caret, defaults.foreground) ?? "#f7768e",
|
|
259
|
+
selectionColor: selection ?? "#515c7e40",
|
|
260
|
+
themeExtension,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function resolveBuiltinTheme(themeName: BuiltinThemeName): CustomTheme {
|
|
265
|
+
const themeExtension = BUILTIN_THEME_EXTENSIONS[themeName];
|
|
266
|
+
if (themeName === "white") {
|
|
267
|
+
return {
|
|
268
|
+
...WHITE_THEME_COLORS,
|
|
269
|
+
themeExtension,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (themeName === "tokyo-night") {
|
|
273
|
+
return {
|
|
274
|
+
...TOKYO_NIGHT_THEME_COLORS,
|
|
275
|
+
themeExtension,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (themeName === "tokyo-night-day") {
|
|
279
|
+
return {
|
|
280
|
+
...TOKYO_NIGHT_DAY_THEME_COLORS,
|
|
281
|
+
themeExtension,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return themeFromDefaults(BUILTIN_THEME_DEFAULTS[themeName], themeExtension);
|
|
285
|
+
}
|
|
286
|
+
|
|
24
287
|
export function createEditorState({
|
|
25
288
|
schema,
|
|
26
289
|
readOnly,
|
|
27
290
|
json,
|
|
28
291
|
theme,
|
|
292
|
+
otherExtension,
|
|
29
293
|
}: CreateEditorStateParams): EditorState {
|
|
30
294
|
const idToRecordDef: { [id: string]: RecordDefinition } = {};
|
|
31
295
|
for (const record of schema.records) {
|
|
@@ -33,34 +297,10 @@ export function createEditorState({
|
|
|
33
297
|
}
|
|
34
298
|
const content = json ?? makeJsonTemplate(schema.type, idToRecordDef);
|
|
35
299
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
backgroundColor: "#1a1b26",
|
|
41
|
-
lighterBgColor: "#1f2335",
|
|
42
|
-
borderColor: "#414868",
|
|
43
|
-
foregroundColor: "#c0caf5",
|
|
44
|
-
accentColor: "#7aa2f7",
|
|
45
|
-
errorColor: "#f7768e",
|
|
46
|
-
selectionColor: "#515c7e40",
|
|
47
|
-
themeExtension: tokyoNight,
|
|
48
|
-
};
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
case "tokyo-night-day": {
|
|
52
|
-
theme = {
|
|
53
|
-
backgroundColor: "#d5d6db",
|
|
54
|
-
lighterBgColor: "#e1e2e7",
|
|
55
|
-
borderColor: "#adb0bb",
|
|
56
|
-
foregroundColor: "#3760bf",
|
|
57
|
-
accentColor: "#2e7de9",
|
|
58
|
-
errorColor: "#f52a65",
|
|
59
|
-
selectionColor: "#3760bf33",
|
|
60
|
-
themeExtension: tokyoNightDay,
|
|
61
|
-
};
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
300
|
+
if (theme === undefined) {
|
|
301
|
+
theme = resolveBuiltinTheme("tokyo-night");
|
|
302
|
+
} else if (typeof theme === "string") {
|
|
303
|
+
theme = resolveBuiltinTheme(theme);
|
|
64
304
|
}
|
|
65
305
|
|
|
66
306
|
return EditorState.create({
|
|
@@ -262,6 +502,7 @@ export function createEditorState({
|
|
|
262
502
|
borderTop: "1px solid",
|
|
263
503
|
},
|
|
264
504
|
}),
|
|
505
|
+
otherExtension ?? [],
|
|
265
506
|
],
|
|
266
507
|
});
|
|
267
508
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Diagnostic } from "@codemirror/lint";
|
|
2
2
|
import { EditorView } from "@codemirror/view";
|
|
3
|
+
import { makeJsonTemplate } from "../json/to_json";
|
|
3
4
|
import { Hint, JsonError, JsonLiteral, TypeHint } from "../json/types";
|
|
4
|
-
import {
|
|
5
|
+
import { jsonStateField } from "./json_state";
|
|
5
6
|
|
|
6
7
|
export function jsonLinter(
|
|
7
8
|
editable: "editable" | "read-only",
|
|
@@ -221,8 +222,6 @@ function getTimestampControlRows(
|
|
|
221
222
|
insert: newJsonString,
|
|
222
223
|
},
|
|
223
224
|
});
|
|
224
|
-
|
|
225
|
-
ensureJsonState(view, (view as any).schema);
|
|
226
225
|
};
|
|
227
226
|
|
|
228
227
|
// Enter key handler for unix_millis input
|
|
@@ -300,6 +299,84 @@ function getTimestampControlRows(
|
|
|
300
299
|
return [headerRow, controlsRow];
|
|
301
300
|
}
|
|
302
301
|
|
|
302
|
+
function getEnumDropdown(
|
|
303
|
+
view: EditorView,
|
|
304
|
+
typeHint: TypeHint,
|
|
305
|
+
enumDefinition: NonNullable<TypeHint["enumDefinition"]>,
|
|
306
|
+
): HTMLDivElement {
|
|
307
|
+
const controlsRow = document.createElement("div");
|
|
308
|
+
controlsRow.className = "cm-diagnostic-controls";
|
|
309
|
+
|
|
310
|
+
const label = document.createElement("span");
|
|
311
|
+
label.className = "cm-diagnostic-label";
|
|
312
|
+
label.textContent = "Variant:";
|
|
313
|
+
|
|
314
|
+
const select = document.createElement("select");
|
|
315
|
+
select.className = "cm-diagnostic-input";
|
|
316
|
+
|
|
317
|
+
{
|
|
318
|
+
// Add the UNKNOWN variant
|
|
319
|
+
const option = document.createElement("option");
|
|
320
|
+
option.value = "UNKNOWN";
|
|
321
|
+
option.textContent = "UNKNOWN";
|
|
322
|
+
select.appendChild(option);
|
|
323
|
+
}
|
|
324
|
+
for (const variant of enumDefinition.variants) {
|
|
325
|
+
const option = document.createElement("option");
|
|
326
|
+
option.value = variant.name;
|
|
327
|
+
option.textContent = variant.name;
|
|
328
|
+
select.appendChild(option);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const currentValue = typeHint.valueContext?.value;
|
|
332
|
+
if (
|
|
333
|
+
currentValue &&
|
|
334
|
+
currentValue.kind === "literal" &&
|
|
335
|
+
currentValue.type === "string"
|
|
336
|
+
) {
|
|
337
|
+
try {
|
|
338
|
+
select.value = JSON.parse(currentValue.jsonCode) as string;
|
|
339
|
+
} catch {
|
|
340
|
+
// Ignore invalid value and keep the default selection.
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
select.addEventListener("change", () => {
|
|
345
|
+
const variant = enumDefinition.variants.find(
|
|
346
|
+
(variant) => variant.name === select.value,
|
|
347
|
+
)!;
|
|
348
|
+
let newJsonString: string;
|
|
349
|
+
if (variant.type) {
|
|
350
|
+
const jsonState = view.state.field(jsonStateField, false)!;
|
|
351
|
+
const valueJson = makeJsonTemplate(
|
|
352
|
+
variant.type,
|
|
353
|
+
jsonState.recordIdToDefinition,
|
|
354
|
+
);
|
|
355
|
+
newJsonString = JSON.stringify(
|
|
356
|
+
{
|
|
357
|
+
kind: variant.name,
|
|
358
|
+
value: valueJson,
|
|
359
|
+
},
|
|
360
|
+
null,
|
|
361
|
+
2,
|
|
362
|
+
).replaceAll("\n", "\n" + " ".repeat(typeHint.valueContext.value.indent));
|
|
363
|
+
} else {
|
|
364
|
+
newJsonString = JSON.stringify(variant.name);
|
|
365
|
+
}
|
|
366
|
+
view.dispatch({
|
|
367
|
+
changes: {
|
|
368
|
+
from: typeHint.segment.start,
|
|
369
|
+
to: typeHint.segment.end,
|
|
370
|
+
insert: newJsonString,
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
controlsRow.appendChild(label);
|
|
376
|
+
controlsRow.appendChild(select);
|
|
377
|
+
return controlsRow;
|
|
378
|
+
}
|
|
379
|
+
|
|
303
380
|
function hintToDiagnostic(
|
|
304
381
|
typeHint: Hint,
|
|
305
382
|
editable: "editable" | "read-only",
|
|
@@ -337,13 +414,21 @@ function hintToDiagnostic(
|
|
|
337
414
|
// Render a timestamp editing control for timestamp hints.
|
|
338
415
|
rows = getTimestampControlRows(view, typeHint);
|
|
339
416
|
} else {
|
|
340
|
-
// Display the message
|
|
417
|
+
// Display the message.
|
|
341
418
|
const pieces = typeof message === "string" ? [message] : message;
|
|
342
419
|
rows = pieces.map((piece) => {
|
|
343
420
|
const row = document.createElement("div");
|
|
344
421
|
row.textContent = piece;
|
|
345
422
|
return row;
|
|
346
423
|
});
|
|
424
|
+
if (
|
|
425
|
+
typeHint.valueContext &&
|
|
426
|
+
typeHint.enumDefinition &&
|
|
427
|
+
editable === "editable"
|
|
428
|
+
) {
|
|
429
|
+
// Render a dropdown for selecting a different variant.
|
|
430
|
+
rows.push(getEnumDropdown(view, typeHint, typeHint.enumDefinition));
|
|
431
|
+
}
|
|
347
432
|
}
|
|
348
433
|
|
|
349
434
|
for (const row of rows) {
|
|
@@ -9,6 +9,7 @@ import { parseJsonValue } from "../json/json_parser";
|
|
|
9
9
|
import { validateSchema } from "../json/schema_validator";
|
|
10
10
|
import type {
|
|
11
11
|
JsonParseResult,
|
|
12
|
+
RecordDefinition,
|
|
12
13
|
TypeDefinition,
|
|
13
14
|
ValidationResult,
|
|
14
15
|
} from "../json/types";
|
|
@@ -17,6 +18,8 @@ export interface JsonState {
|
|
|
17
18
|
readonly parseResult: JsonParseResult;
|
|
18
19
|
readonly validationResult?: ValidationResult;
|
|
19
20
|
readonly source: string;
|
|
21
|
+
readonly schema: TypeDefinition;
|
|
22
|
+
readonly recordIdToDefinition: { [id: string]: RecordDefinition };
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
const updateJsonState = StateEffect.define<JsonState>();
|
|
@@ -58,7 +61,15 @@ export function ensureJsonState(
|
|
|
58
61
|
validationResult = validateSchema(parseResult.value, schema);
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
const
|
|
64
|
+
const recordIdToDefinition = indexRecordDefinitions(schema, currentState);
|
|
65
|
+
|
|
66
|
+
const newState: JsonState = {
|
|
67
|
+
parseResult,
|
|
68
|
+
validationResult,
|
|
69
|
+
source,
|
|
70
|
+
schema,
|
|
71
|
+
recordIdToDefinition,
|
|
72
|
+
};
|
|
62
73
|
|
|
63
74
|
// Update the state if it's different
|
|
64
75
|
if (!currentState || currentState !== newState) {
|
|
@@ -139,21 +150,37 @@ export function debouncedJsonParser(schema: TypeDefinition): Extension[] {
|
|
|
139
150
|
insert: edit.replacement,
|
|
140
151
|
}));
|
|
141
152
|
|
|
153
|
+
const oldState = this.view.state.field(jsonStateField, false);
|
|
154
|
+
const recordIdToDefinition = indexRecordDefinitions(
|
|
155
|
+
schema,
|
|
156
|
+
oldState,
|
|
157
|
+
);
|
|
158
|
+
|
|
142
159
|
this.view.dispatch({
|
|
143
160
|
changes,
|
|
144
161
|
effects: updateJsonState.of({
|
|
145
162
|
parseResult,
|
|
146
163
|
validationResult,
|
|
147
164
|
source,
|
|
165
|
+
schema,
|
|
166
|
+
recordIdToDefinition,
|
|
148
167
|
}),
|
|
149
168
|
scrollIntoView: true,
|
|
150
169
|
});
|
|
151
170
|
} else {
|
|
171
|
+
const oldState = this.view.state.field(jsonStateField, false);
|
|
172
|
+
const recordIdToDefinition = indexRecordDefinitions(
|
|
173
|
+
schema,
|
|
174
|
+
oldState,
|
|
175
|
+
);
|
|
176
|
+
|
|
152
177
|
this.view.dispatch({
|
|
153
178
|
effects: updateJsonState.of({
|
|
154
179
|
parseResult,
|
|
155
180
|
validationResult,
|
|
156
181
|
source,
|
|
182
|
+
schema,
|
|
183
|
+
recordIdToDefinition,
|
|
157
184
|
}),
|
|
158
185
|
});
|
|
159
186
|
}
|
|
@@ -168,3 +195,19 @@ export function debouncedJsonParser(schema: TypeDefinition): Extension[] {
|
|
|
168
195
|
),
|
|
169
196
|
];
|
|
170
197
|
}
|
|
198
|
+
|
|
199
|
+
function indexRecordDefinitions(
|
|
200
|
+
schema: TypeDefinition,
|
|
201
|
+
oldState: JsonState | null | undefined,
|
|
202
|
+
): {
|
|
203
|
+
[id: string]: RecordDefinition;
|
|
204
|
+
} {
|
|
205
|
+
if (schema === oldState?.schema) {
|
|
206
|
+
return oldState.recordIdToDefinition;
|
|
207
|
+
}
|
|
208
|
+
const idToRecordDef: { [id: string]: RecordDefinition } = {};
|
|
209
|
+
for (const recordDef of schema.records) {
|
|
210
|
+
idToRecordDef[recordDef.id] = recordDef;
|
|
211
|
+
}
|
|
212
|
+
return idToRecordDef;
|
|
213
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,18 @@ describe("json_parser", () => {
|
|
|
63
63
|
});
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
+
it("tracks indent for top-level literal", () => {
|
|
67
|
+
expect(parseJsonValue(" true")).toMatch({
|
|
68
|
+
value: {
|
|
69
|
+
kind: "literal",
|
|
70
|
+
jsonCode: "true",
|
|
71
|
+
type: "boolean",
|
|
72
|
+
indent: 3,
|
|
73
|
+
},
|
|
74
|
+
errors: [],
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
66
78
|
it("parses empty array", () => {
|
|
67
79
|
expect(parseJsonValue("[]")).toMatch({
|
|
68
80
|
value: { kind: "array", values: [] },
|
|
@@ -119,6 +131,45 @@ describe("json_parser", () => {
|
|
|
119
131
|
});
|
|
120
132
|
});
|
|
121
133
|
|
|
134
|
+
it("tracks indent from line leading spaces for field values", () => {
|
|
135
|
+
const result = parseJsonValue(`{
|
|
136
|
+
"a": 1,
|
|
137
|
+
"b": [
|
|
138
|
+
2
|
|
139
|
+
]
|
|
140
|
+
}`);
|
|
141
|
+
|
|
142
|
+
expect(result).toMatch({
|
|
143
|
+
value: {
|
|
144
|
+
kind: "object",
|
|
145
|
+
indent: 0,
|
|
146
|
+
keyValues: {
|
|
147
|
+
a: {
|
|
148
|
+
value: {
|
|
149
|
+
kind: "literal",
|
|
150
|
+
jsonCode: "1",
|
|
151
|
+
indent: 2,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
b: {
|
|
155
|
+
value: {
|
|
156
|
+
kind: "array",
|
|
157
|
+
indent: 2,
|
|
158
|
+
values: [
|
|
159
|
+
{
|
|
160
|
+
kind: "literal",
|
|
161
|
+
jsonCode: "2",
|
|
162
|
+
indent: 4,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
errors: [],
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
122
173
|
it("parses arrays with whitespace", () => {
|
|
123
174
|
expect(parseJsonValue(" [ 1 ] ")).toMatch({
|
|
124
175
|
value: {
|