skir-codemirror-plugin 0.9.0

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.
Files changed (72) hide show
  1. package/README.md +126 -0
  2. package/dist/codemirror/create_editor_state.d.ts +20 -0
  3. package/dist/codemirror/create_editor_state.d.ts.map +1 -0
  4. package/dist/codemirror/create_editor_state.js +252 -0
  5. package/dist/codemirror/create_editor_state.js.map +1 -0
  6. package/dist/codemirror/enter_key_handler.d.ts +8 -0
  7. package/dist/codemirror/enter_key_handler.d.ts.map +1 -0
  8. package/dist/codemirror/enter_key_handler.js +181 -0
  9. package/dist/codemirror/enter_key_handler.js.map +1 -0
  10. package/dist/codemirror/json_completion.d.ts +4 -0
  11. package/dist/codemirror/json_completion.d.ts.map +1 -0
  12. package/dist/codemirror/json_completion.js +150 -0
  13. package/dist/codemirror/json_completion.js.map +1 -0
  14. package/dist/codemirror/json_linter.d.ts +4 -0
  15. package/dist/codemirror/json_linter.d.ts.map +1 -0
  16. package/dist/codemirror/json_linter.js +277 -0
  17. package/dist/codemirror/json_linter.js.map +1 -0
  18. package/dist/codemirror/json_state.d.ts +16 -0
  19. package/dist/codemirror/json_state.d.ts.map +1 -0
  20. package/dist/codemirror/json_state.js +124 -0
  21. package/dist/codemirror/json_state.js.map +1 -0
  22. package/dist/codemirror/status_bar.d.ts +3 -0
  23. package/dist/codemirror/status_bar.d.ts.map +1 -0
  24. package/dist/codemirror/status_bar.js +123 -0
  25. package/dist/codemirror/status_bar.js.map +1 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +2 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/json/json_parser.d.ts +3 -0
  31. package/dist/json/json_parser.d.ts.map +1 -0
  32. package/dist/json/json_parser.js +414 -0
  33. package/dist/json/json_parser.js.map +1 -0
  34. package/dist/json/json_parser.test.d.ts +2 -0
  35. package/dist/json/json_parser.test.d.ts.map +1 -0
  36. package/dist/json/json_parser.test.js +337 -0
  37. package/dist/json/json_parser.test.js.map +1 -0
  38. package/dist/json/schema_validator.d.ts +3 -0
  39. package/dist/json/schema_validator.d.ts.map +1 -0
  40. package/dist/json/schema_validator.js +525 -0
  41. package/dist/json/schema_validator.js.map +1 -0
  42. package/dist/json/schema_validator.test.d.ts +2 -0
  43. package/dist/json/schema_validator.test.d.ts.map +1 -0
  44. package/dist/json/schema_validator.test.js +212 -0
  45. package/dist/json/schema_validator.test.js.map +1 -0
  46. package/dist/json/to_json.d.ts +6 -0
  47. package/dist/json/to_json.d.ts.map +1 -0
  48. package/dist/json/to_json.js +61 -0
  49. package/dist/json/to_json.js.map +1 -0
  50. package/dist/json/to_json.test.d.ts +2 -0
  51. package/dist/json/to_json.test.d.ts.map +1 -0
  52. package/dist/json/to_json.test.js +128 -0
  53. package/dist/json/to_json.test.js.map +1 -0
  54. package/dist/json/types.d.ts +170 -0
  55. package/dist/json/types.d.ts.map +1 -0
  56. package/dist/json/types.js +2 -0
  57. package/dist/json/types.js.map +1 -0
  58. package/package.json +85 -0
  59. package/src/codemirror/create_editor_state.ts +278 -0
  60. package/src/codemirror/enter_key_handler.ts +232 -0
  61. package/src/codemirror/json_completion.ts +182 -0
  62. package/src/codemirror/json_linter.ts +358 -0
  63. package/src/codemirror/json_state.ts +170 -0
  64. package/src/codemirror/status_bar.ts +137 -0
  65. package/src/index.ts +6 -0
  66. package/src/json/json_parser.test.ts +360 -0
  67. package/src/json/json_parser.ts +461 -0
  68. package/src/json/schema_validator.test.ts +230 -0
  69. package/src/json/schema_validator.ts +558 -0
  70. package/src/json/to_json.test.ts +150 -0
  71. package/src/json/to_json.ts +70 -0
  72. package/src/json/types.ts +254 -0
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # skir-codemirror-plugin
2
+
3
+ This library provides a CodeMirror-based JSON editor for Skir values.
4
+ It helps users edit Skir request/response payloads in a human-readable JSON form while preserving schema-aware guidance and validation.
5
+
6
+ Demo: `npx skir-studio-demo`
7
+
8
+ ## What It Provides
9
+
10
+ - A ready-to-use `createEditorState(...)` factory for CodeMirror.
11
+ - Schema-driven JSON template generation (when no JSON value is provided).
12
+ - JSON validation and lint diagnostics.
13
+ - In-editor hints and completions informed by the Skir type schema.
14
+ - Read-only mode support for response viewing.
15
+ - Built-in theme support:
16
+ - `tokyo-night`
17
+ - `tokyo-night-day`
18
+ - custom theme object
19
+
20
+ ## Install
21
+
22
+ ```sh
23
+ npm install skir-codemirror-plugin
24
+ ```
25
+
26
+ ## Public API
27
+
28
+ The package root exports:
29
+
30
+ - `createEditorState`
31
+ - `CreateEditorStateParams` (type)
32
+ - `CustomTheme` (type)
33
+ - `Json` (type)
34
+
35
+ ## Usage
36
+
37
+ ```ts
38
+ import { EditorView } from "@codemirror/view";
39
+ import {
40
+ createEditorState,
41
+ type CreateEditorStateParams,
42
+ } from "skir-codemirror-plugin";
43
+
44
+ const params: CreateEditorStateParams = {
45
+ schema: {
46
+ type: { kind: "primitive", value: "string" },
47
+ records: [],
48
+ },
49
+ // Optional:
50
+ // readOnly: true,
51
+ // json: "hello",
52
+ // theme: "tokyo-night-day",
53
+ };
54
+
55
+ const state = createEditorState(params);
56
+
57
+ new EditorView({
58
+ state,
59
+ parent: document.getElementById("editor")!,
60
+ });
61
+ ```
62
+
63
+ ## createEditorState Parameters
64
+
65
+ ```ts
66
+ {
67
+ schema: TypeDefinition,
68
+ readOnly?: true,
69
+ json?: Json,
70
+ theme?: "tokyo-night" | "tokyo-night-day" | CustomTheme,
71
+ }
72
+ ```
73
+
74
+ Behavior:
75
+
76
+ - `schema` is required and drives validation/completion.
77
+ - If `json` is omitted, a JSON template is generated from the schema.
78
+ - `readOnly: true` enables non-editable mode.
79
+ - `theme` defaults to `tokyo-night`.
80
+
81
+ ## Local Dev Flow
82
+
83
+ This repository includes a minimal local dev page for previewing the editor state produced by `createEditorState`.
84
+
85
+ ### Run It
86
+
87
+ ```sh
88
+ npm run dev
89
+ ```
90
+
91
+ This does three things:
92
+
93
+ 1. Builds TypeScript sources into `dist/`.
94
+ 2. Builds the dev entry point from `dev/main.ts` into `dev-dist/main.js`.
95
+ 3. Starts `web-dev-server` and opens `/dev/index.html`.
96
+
97
+ Dev URL:
98
+
99
+ ```text
100
+ http://localhost:8080/dev/index.html
101
+ ```
102
+
103
+ ### Dev Files
104
+
105
+ - `dev/index.html`: minimal host page containing only `#editor` and module import.
106
+ - `dev/main.ts`: creates `EditorView` with `createEditorState(...)`.
107
+ - `dev-dist/main.js`: generated browser output from `dev/main.ts`.
108
+
109
+ To try your own schema/JSON inputs, edit `dev/main.ts` and change the `params` object.
110
+
111
+ ### Auto Rebuild and Reload
112
+
113
+ `npm run dev` watches both:
114
+
115
+ - `src/**/*.ts` -> rebuilds the library into `dist/`
116
+ - `dev/**/*.ts` -> rebuilds the dev entry into `dev-dist/`
117
+
118
+ `web-dev-server --watch` reloads the page when served files change.
119
+
120
+ ### Dev-Only Note
121
+
122
+ This flow is for local development only.
123
+
124
+ - Dev sources are in `dev/`.
125
+ - Dev compiled output is in `dev-dist/`.
126
+ - Package distribution uses `dist/` as the runtime entrypoint.
@@ -0,0 +1,20 @@
1
+ import { EditorState, Extension } from "@codemirror/state";
2
+ import type { Json, TypeDefinition } from "../json/types";
3
+ export type CreateEditorStateParams = {
4
+ schema: TypeDefinition;
5
+ readOnly?: true;
6
+ json?: Json;
7
+ theme?: "tokyo-night" | "tokyo-night-day" | CustomTheme;
8
+ };
9
+ export declare function createEditorState({ schema, readOnly, json, theme, }: CreateEditorStateParams): EditorState;
10
+ export interface CustomTheme {
11
+ backgroundColor: string;
12
+ lighterBgColor: string;
13
+ borderColor: string;
14
+ foregroundColor: string;
15
+ accentColor: string;
16
+ errorColor: string;
17
+ selectionColor: string;
18
+ themeExtension?: Extension;
19
+ }
20
+ //# sourceMappingURL=create_editor_state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_editor_state.d.ts","sourceRoot":"","sources":["../../src/codemirror/create_editor_state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAM3D,OAAO,KAAK,EAAE,IAAI,EAAoB,cAAc,EAAE,MAAM,eAAe,CAAC;AAO5E,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,aAAa,GAAG,iBAAiB,GAAG,WAAW,CAAC;CACzD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,KAAK,GACN,EAAE,uBAAuB,GAAG,WAAW,CA8OvC;AAED,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B"}
@@ -0,0 +1,252 @@
1
+ import { autocompletion, closeBrackets } from "@codemirror/autocomplete";
2
+ import { json as jsonExtension } from "@codemirror/lang-json";
3
+ import { linter, lintGutter } from "@codemirror/lint";
4
+ import { EditorState } from "@codemirror/state";
5
+ import { EditorView } from "@codemirror/view";
6
+ import { tokyoNight } from "@uiw/codemirror-theme-tokyo-night";
7
+ import { tokyoNightDay } from "@uiw/codemirror-theme-tokyo-night-day";
8
+ import { basicSetup } from "codemirror";
9
+ import { makeJsonTemplate } from "../json/to_json";
10
+ import { enterKeyHandler } from "./enter_key_handler";
11
+ import { jsonCompletion } from "./json_completion";
12
+ import { jsonLinter } from "./json_linter";
13
+ import { debouncedJsonParser } from "./json_state";
14
+ import { statusBar } from "./status_bar";
15
+ export function createEditorState({ schema, readOnly, json, theme, }) {
16
+ const idToRecordDef = {};
17
+ for (const record of schema.records) {
18
+ idToRecordDef[record.id] = record;
19
+ }
20
+ const content = json ?? makeJsonTemplate(schema.type, idToRecordDef);
21
+ switch (theme) {
22
+ case undefined:
23
+ case "tokyo-night": {
24
+ theme = {
25
+ backgroundColor: "#1a1b26",
26
+ lighterBgColor: "#1f2335",
27
+ borderColor: "#414868",
28
+ foregroundColor: "#c0caf5",
29
+ accentColor: "#7aa2f7",
30
+ errorColor: "#f7768e",
31
+ selectionColor: "#515c7e40",
32
+ themeExtension: tokyoNight,
33
+ };
34
+ break;
35
+ }
36
+ case "tokyo-night-day": {
37
+ theme = {
38
+ backgroundColor: "#d5d6db",
39
+ lighterBgColor: "#e1e2e7",
40
+ borderColor: "#adb0bb",
41
+ foregroundColor: "#3760bf",
42
+ accentColor: "#2e7de9",
43
+ errorColor: "#f52a65",
44
+ selectionColor: "#3760bf33",
45
+ themeExtension: tokyoNightDay,
46
+ };
47
+ break;
48
+ }
49
+ }
50
+ return EditorState.create({
51
+ doc: JSON.stringify(content, null, 2),
52
+ extensions: [
53
+ EditorState.readOnly.of(!!readOnly),
54
+ enterKeyHandler(schema),
55
+ basicSetup,
56
+ EditorState.languageData.of(() => [
57
+ {
58
+ closeBrackets: { before: ",]}" },
59
+ },
60
+ ]),
61
+ closeBrackets(),
62
+ theme.themeExtension ?? [],
63
+ jsonExtension(),
64
+ debouncedJsonParser(schema),
65
+ linter(jsonLinter(readOnly ? "read-only" : "editable")),
66
+ autocompletion({
67
+ override: [jsonCompletion(schema)],
68
+ }),
69
+ lintGutter(),
70
+ statusBar(),
71
+ EditorView.theme({
72
+ "&": {
73
+ fontSize: "14px",
74
+ height: "100%",
75
+ },
76
+ ".cm-scroller": {
77
+ fontFamily: "'JetBrains Mono', monospace",
78
+ overflow: "auto",
79
+ },
80
+ ".cm-lintRange-info": {
81
+ backgroundImage: "none",
82
+ },
83
+ ".cm-lintRange-info:hover": {
84
+ backgroundColor: theme.selectionColor,
85
+ },
86
+ ".cm-lintRange-error": {
87
+ backgroundImage: "none",
88
+ borderBottom: `3px solid ${theme.errorColor}`,
89
+ },
90
+ ".cm-tooltip-hover": {
91
+ backgroundColor: theme.lighterBgColor,
92
+ border: `1px solid ${theme.borderColor}`,
93
+ borderRadius: "4px",
94
+ padding: "8px 12px",
95
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.5)",
96
+ fontSize: "14px",
97
+ lineHeight: "1.4",
98
+ color: theme.foregroundColor,
99
+ },
100
+ ".cm-tooltip.cm-tooltip-lint": {
101
+ backgroundColor: theme.lighterBgColor,
102
+ border: `1px solid ${theme.borderColor}`,
103
+ borderRadius: "4px",
104
+ padding: "8px 12px",
105
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.5)",
106
+ fontSize: "14px", // Ensure consistency
107
+ color: theme.foregroundColor,
108
+ },
109
+ ".cm-tooltip-lint .cm-diagnostic-error": {
110
+ fontWeight: "bold",
111
+ color: theme.errorColor,
112
+ },
113
+ ".cm-status-bar": {
114
+ backgroundColor: theme.lighterBgColor,
115
+ borderTopColor: theme.borderColor,
116
+ color: theme.accentColor,
117
+ },
118
+ ".cm-diagnostic-textarea": {
119
+ backgroundColor: theme.lighterBgColor,
120
+ borderColor: theme.borderColor,
121
+ color: theme.foregroundColor,
122
+ },
123
+ ".cm-diagnostic-textarea[readonly]": {
124
+ backgroundColor: theme.backgroundColor,
125
+ },
126
+ ".cm-diagnostic-input": {
127
+ backgroundColor: theme.lighterBgColor,
128
+ borderColor: theme.borderColor,
129
+ color: theme.foregroundColor,
130
+ },
131
+ ".cm-diagnostic-button": {
132
+ backgroundColor: theme.lighterBgColor,
133
+ borderColor: theme.borderColor,
134
+ color: theme.foregroundColor,
135
+ },
136
+ ".cm-diagnostic-button:hover": {
137
+ backgroundColor: theme.borderColor,
138
+ },
139
+ ".cm-diagnostic-error-message": {
140
+ color: theme.errorColor,
141
+ },
142
+ ".diagnostic-row + .diagnostic-row": {
143
+ borderTopColor: theme.borderColor,
144
+ },
145
+ }),
146
+ EditorView.baseTheme({
147
+ ".cm-lint-marker-info": {
148
+ display: "none",
149
+ },
150
+ ".cm-status-bar": {
151
+ display: "flex",
152
+ flexDirection: "row-reverse",
153
+ justifyContent: "flex-end",
154
+ padding: "4px 12px",
155
+ borderTop: "1px solid",
156
+ fontSize: "12px",
157
+ height: "16px",
158
+ },
159
+ ".cm-status-bar-link": {
160
+ textDecoration: "none",
161
+ cursor: "pointer",
162
+ },
163
+ ".cm-status-bar-link:hover": {
164
+ textDecoration: "underline",
165
+ },
166
+ ".cm-status-bar-link:hover ~ .cm-status-bar-link": {
167
+ textDecoration: "underline",
168
+ },
169
+ ".cm-diagnostic-wrapper": {
170
+ fontSize: "12px",
171
+ lineHeight: "1.3",
172
+ padding: "2px",
173
+ },
174
+ ".cm-diagnostic-controls": {
175
+ marginTop: "4px",
176
+ display: "flex",
177
+ gap: "6px",
178
+ alignItems: "center",
179
+ },
180
+ ".cm-diagnostic-label": {
181
+ whiteSpace: "nowrap",
182
+ fontWeight: "500",
183
+ minWidth: "64px",
184
+ },
185
+ ".cm-diagnostic-textarea": {
186
+ flex: "1",
187
+ padding: "3px 6px",
188
+ border: "1px solid",
189
+ borderRadius: "3px",
190
+ fontSize: "12px",
191
+ fontFamily: "'JetBrains Mono', monospace",
192
+ resize: "none",
193
+ overflow: "auto",
194
+ boxSizing: "border-box",
195
+ },
196
+ ".cm-diagnostic-textarea[readonly]": {
197
+ cursor: "default",
198
+ },
199
+ ".cm-diagnostic-input": {
200
+ padding: "3px 6px",
201
+ border: "1px solid",
202
+ borderRadius: "3px",
203
+ fontSize: "12px",
204
+ fontFamily: "'JetBrains Mono', monospace",
205
+ boxSizing: "border-box",
206
+ width: "100%",
207
+ },
208
+ ".cm-diagnostic-button": {
209
+ padding: "3px 12px",
210
+ border: "1px solid",
211
+ borderRadius: "3px",
212
+ fontSize: "12px",
213
+ fontFamily: "'JetBrains Mono', monospace",
214
+ cursor: "pointer",
215
+ boxSizing: "border-box",
216
+ },
217
+ ".cm-diagnostic-button:active": {
218
+ transform: "translateY(1px)",
219
+ },
220
+ ".cm-diagnostic-error-message": {
221
+ fontSize: "11px",
222
+ marginTop: "2px",
223
+ fontWeight: "500",
224
+ },
225
+ ".cm-timestamp-field": {
226
+ display: "flex",
227
+ flexDirection: "column",
228
+ gap: "2px",
229
+ flex: "1",
230
+ },
231
+ ".cm-tooltip-lint .cm-diagnostic": {
232
+ padding: "0",
233
+ borderTop: "none",
234
+ borderLeft: "none",
235
+ },
236
+ ".cm-tooltip-lint .cm-diagnostic-error": {
237
+ borderLeft: "none",
238
+ fontWeight: "bold",
239
+ },
240
+ ".cm-tooltip-lint .cm-diagnostic-info": {
241
+ borderLeft: "none",
242
+ },
243
+ ".diagnostic-row + .diagnostic-row": {
244
+ marginTop: "8px",
245
+ paddingTop: "8px",
246
+ borderTop: "1px solid",
247
+ },
248
+ }),
249
+ ],
250
+ });
251
+ }
252
+ //# sourceMappingURL=create_editor_state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_editor_state.js","sourceRoot":"","sources":["../../src/codemirror/create_editor_state.ts"],"names":[],"mappings":"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","sourcesContent":["import { autocompletion, closeBrackets } from \"@codemirror/autocomplete\";\nimport { json as jsonExtension } from \"@codemirror/lang-json\";\nimport { linter, lintGutter } from \"@codemirror/lint\";\nimport { EditorState, Extension } from \"@codemirror/state\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tokyoNight } from \"@uiw/codemirror-theme-tokyo-night\";\nimport { tokyoNightDay } from \"@uiw/codemirror-theme-tokyo-night-day\";\nimport { basicSetup } from \"codemirror\";\nimport { makeJsonTemplate } from \"../json/to_json\";\nimport type { Json, RecordDefinition, TypeDefinition } from \"../json/types\";\nimport { enterKeyHandler } from \"./enter_key_handler\";\nimport { jsonCompletion } from \"./json_completion\";\nimport { jsonLinter } from \"./json_linter\";\nimport { debouncedJsonParser } from \"./json_state\";\nimport { statusBar } from \"./status_bar\";\n\nexport type CreateEditorStateParams = {\n schema: TypeDefinition;\n readOnly?: true;\n json?: Json;\n theme?: \"tokyo-night\" | \"tokyo-night-day\" | CustomTheme;\n};\n\nexport function createEditorState({\n schema,\n readOnly,\n json,\n theme,\n}: CreateEditorStateParams): EditorState {\n const idToRecordDef: { [id: string]: RecordDefinition } = {};\n for (const record of schema.records) {\n idToRecordDef[record.id] = record;\n }\n const content = json ?? makeJsonTemplate(schema.type, idToRecordDef);\n\n switch (theme) {\n case undefined:\n case \"tokyo-night\": {\n theme = {\n backgroundColor: \"#1a1b26\",\n lighterBgColor: \"#1f2335\",\n borderColor: \"#414868\",\n foregroundColor: \"#c0caf5\",\n accentColor: \"#7aa2f7\",\n errorColor: \"#f7768e\",\n selectionColor: \"#515c7e40\",\n themeExtension: tokyoNight,\n };\n break;\n }\n case \"tokyo-night-day\": {\n theme = {\n backgroundColor: \"#d5d6db\",\n lighterBgColor: \"#e1e2e7\",\n borderColor: \"#adb0bb\",\n foregroundColor: \"#3760bf\",\n accentColor: \"#2e7de9\",\n errorColor: \"#f52a65\",\n selectionColor: \"#3760bf33\",\n themeExtension: tokyoNightDay,\n };\n break;\n }\n }\n\n return EditorState.create({\n doc: JSON.stringify(content, null, 2),\n extensions: [\n EditorState.readOnly.of(!!readOnly),\n enterKeyHandler(schema),\n basicSetup,\n EditorState.languageData.of(() => [\n {\n closeBrackets: { before: \",]}\" },\n },\n ]),\n closeBrackets(),\n theme.themeExtension ?? [],\n jsonExtension(),\n debouncedJsonParser(schema),\n linter(jsonLinter(readOnly ? \"read-only\" : \"editable\")),\n autocompletion({\n override: [jsonCompletion(schema)],\n }),\n lintGutter(),\n statusBar(),\n EditorView.theme({\n \"&\": {\n fontSize: \"14px\",\n height: \"100%\",\n },\n \".cm-scroller\": {\n fontFamily: \"'JetBrains Mono', monospace\",\n overflow: \"auto\",\n },\n \".cm-lintRange-info\": {\n backgroundImage: \"none\",\n },\n \".cm-lintRange-info:hover\": {\n backgroundColor: theme.selectionColor,\n },\n \".cm-lintRange-error\": {\n backgroundImage: \"none\",\n borderBottom: `3px solid ${theme.errorColor}`,\n },\n \".cm-tooltip-hover\": {\n backgroundColor: theme.lighterBgColor,\n border: `1px solid ${theme.borderColor}`,\n borderRadius: \"4px\",\n padding: \"8px 12px\",\n boxShadow: \"0 4px 12px rgba(0, 0, 0, 0.5)\",\n fontSize: \"14px\",\n lineHeight: \"1.4\",\n color: theme.foregroundColor,\n },\n \".cm-tooltip.cm-tooltip-lint\": {\n backgroundColor: theme.lighterBgColor,\n border: `1px solid ${theme.borderColor}`,\n borderRadius: \"4px\",\n padding: \"8px 12px\",\n boxShadow: \"0 4px 12px rgba(0, 0, 0, 0.5)\",\n fontSize: \"14px\", // Ensure consistency\n color: theme.foregroundColor,\n },\n \".cm-tooltip-lint .cm-diagnostic-error\": {\n fontWeight: \"bold\",\n color: theme.errorColor,\n },\n \".cm-status-bar\": {\n backgroundColor: theme.lighterBgColor,\n borderTopColor: theme.borderColor,\n color: theme.accentColor,\n },\n \".cm-diagnostic-textarea\": {\n backgroundColor: theme.lighterBgColor,\n borderColor: theme.borderColor,\n color: theme.foregroundColor,\n },\n \".cm-diagnostic-textarea[readonly]\": {\n backgroundColor: theme.backgroundColor,\n },\n \".cm-diagnostic-input\": {\n backgroundColor: theme.lighterBgColor,\n borderColor: theme.borderColor,\n color: theme.foregroundColor,\n },\n \".cm-diagnostic-button\": {\n backgroundColor: theme.lighterBgColor,\n borderColor: theme.borderColor,\n color: theme.foregroundColor,\n },\n \".cm-diagnostic-button:hover\": {\n backgroundColor: theme.borderColor,\n },\n \".cm-diagnostic-error-message\": {\n color: theme.errorColor,\n },\n \".diagnostic-row + .diagnostic-row\": {\n borderTopColor: theme.borderColor,\n },\n }),\n EditorView.baseTheme({\n \".cm-lint-marker-info\": {\n display: \"none\",\n },\n \".cm-status-bar\": {\n display: \"flex\",\n flexDirection: \"row-reverse\",\n justifyContent: \"flex-end\",\n padding: \"4px 12px\",\n borderTop: \"1px solid\",\n fontSize: \"12px\",\n height: \"16px\",\n },\n \".cm-status-bar-link\": {\n textDecoration: \"none\",\n cursor: \"pointer\",\n },\n \".cm-status-bar-link:hover\": {\n textDecoration: \"underline\",\n },\n \".cm-status-bar-link:hover ~ .cm-status-bar-link\": {\n textDecoration: \"underline\",\n },\n \".cm-diagnostic-wrapper\": {\n fontSize: \"12px\",\n lineHeight: \"1.3\",\n padding: \"2px\",\n },\n \".cm-diagnostic-controls\": {\n marginTop: \"4px\",\n display: \"flex\",\n gap: \"6px\",\n alignItems: \"center\",\n },\n \".cm-diagnostic-label\": {\n whiteSpace: \"nowrap\",\n fontWeight: \"500\",\n minWidth: \"64px\",\n },\n \".cm-diagnostic-textarea\": {\n flex: \"1\",\n padding: \"3px 6px\",\n border: \"1px solid\",\n borderRadius: \"3px\",\n fontSize: \"12px\",\n fontFamily: \"'JetBrains Mono', monospace\",\n resize: \"none\",\n overflow: \"auto\",\n boxSizing: \"border-box\",\n },\n \".cm-diagnostic-textarea[readonly]\": {\n cursor: \"default\",\n },\n \".cm-diagnostic-input\": {\n padding: \"3px 6px\",\n border: \"1px solid\",\n borderRadius: \"3px\",\n fontSize: \"12px\",\n fontFamily: \"'JetBrains Mono', monospace\",\n boxSizing: \"border-box\",\n width: \"100%\",\n },\n \".cm-diagnostic-button\": {\n padding: \"3px 12px\",\n border: \"1px solid\",\n borderRadius: \"3px\",\n fontSize: \"12px\",\n fontFamily: \"'JetBrains Mono', monospace\",\n cursor: \"pointer\",\n boxSizing: \"border-box\",\n },\n \".cm-diagnostic-button:active\": {\n transform: \"translateY(1px)\",\n },\n \".cm-diagnostic-error-message\": {\n fontSize: \"11px\",\n marginTop: \"2px\",\n fontWeight: \"500\",\n },\n \".cm-timestamp-field\": {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"2px\",\n flex: \"1\",\n },\n \".cm-tooltip-lint .cm-diagnostic\": {\n padding: \"0\",\n borderTop: \"none\",\n borderLeft: \"none\",\n },\n \".cm-tooltip-lint .cm-diagnostic-error\": {\n borderLeft: \"none\",\n fontWeight: \"bold\",\n },\n \".cm-tooltip-lint .cm-diagnostic-info\": {\n borderLeft: \"none\",\n },\n \".diagnostic-row + .diagnostic-row\": {\n marginTop: \"8px\",\n paddingTop: \"8px\",\n borderTop: \"1px solid\",\n },\n }),\n ],\n });\n}\n\nexport interface CustomTheme {\n backgroundColor: string;\n lighterBgColor: string;\n borderColor: string;\n foregroundColor: string;\n accentColor: string;\n errorColor: string;\n selectionColor: string;\n themeExtension?: Extension;\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { Extension } from "@codemirror/state";
2
+ import { TypeDefinition } from "../json/types";
3
+ /**
4
+ * Triggered when the Enter key is pressed between 2 consecutive curly braces or
5
+ * square brackets.
6
+ */
7
+ export declare function enterKeyHandler(schema: TypeDefinition): Extension;
8
+ //# sourceMappingURL=enter_key_handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enter_key_handler.d.ts","sourceRoot":"","sources":["../../src/codemirror/enter_key_handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,OAAO,EAKL,cAAc,EACf,MAAM,eAAe,CAAC;AAGvB;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAmGjE"}
@@ -0,0 +1,181 @@
1
+ import { keymap } from "@codemirror/view";
2
+ import { makeJsonTemplate } from "../json/to_json";
3
+ import { ensureJsonState } from "./json_state";
4
+ /**
5
+ * Triggered when the Enter key is pressed between 2 consecutive curly braces or
6
+ * square brackets.
7
+ */
8
+ export function enterKeyHandler(schema) {
9
+ return keymap.of([
10
+ {
11
+ key: "Enter",
12
+ run: (view) => {
13
+ const { state } = view;
14
+ const { selection } = state;
15
+ const { main } = selection;
16
+ // Get the current cursor position
17
+ const cursorPos = main.head;
18
+ const textAround = state.doc.sliceString(cursorPos - 1, cursorPos + 1);
19
+ if (textAround !== "{}" && textAround !== "[]") {
20
+ return false; // Allow default Enter behavior
21
+ }
22
+ // Ensure JSON state is up-to-date
23
+ const jsonState = ensureJsonState(view, schema);
24
+ const parseResult = jsonState.parseResult;
25
+ if (!parseResult.value) {
26
+ return false;
27
+ }
28
+ const jsonValue = parseResult.value;
29
+ const idToRecordDef = {};
30
+ for (const record of schema.records) {
31
+ idToRecordDef[record.id] = record;
32
+ }
33
+ const type = findTypeByLeftBracketPos(jsonValue, cursorPos - 1, idToRecordDef);
34
+ if (type === null) {
35
+ return false;
36
+ }
37
+ let codeToInsert;
38
+ if (type.kind === "array") {
39
+ codeToInsert = JSON.stringify(makeJsonTemplate(type.value.item, idToRecordDef), null, 2);
40
+ }
41
+ else if (type.kind === "record") {
42
+ const recordDef = idToRecordDef[type.value];
43
+ if (recordDef.kind === "struct") {
44
+ codeToInsert = JSON.stringify(makeJsonTemplate(type, idToRecordDef), null, 2);
45
+ if (codeToInsert === "{}") {
46
+ return false;
47
+ }
48
+ // Remove the curly brackets at the start and end of the string and
49
+ // unindent each line.
50
+ codeToInsert = codeToInsert
51
+ .split("\n")
52
+ .slice(1, -1)
53
+ .map((line) => line.substring(2))
54
+ .join("\n");
55
+ }
56
+ else {
57
+ // Enum
58
+ codeToInsert = ['"kind": "",', '"value": {}'].join("\n");
59
+ }
60
+ }
61
+ else if (type.kind === "timestamp") {
62
+ codeToInsert =
63
+ '"unix_millis": 0,\n"formatted": "1970-01-01T00:00:00.000Z"';
64
+ }
65
+ else {
66
+ const _ = type;
67
+ throw new Error(_);
68
+ }
69
+ // Indent the code to insert based on the current line's indentation
70
+ const lineIndentation = getLineIndentation(state.doc.toString(), cursorPos - 1);
71
+ codeToInsert = indent(codeToInsert, " " + lineIndentation);
72
+ codeToInsert = `\n${codeToInsert}\n${lineIndentation}`;
73
+ // Apply the transaction
74
+ view.dispatch({
75
+ changes: {
76
+ from: cursorPos,
77
+ to: cursorPos,
78
+ insert: codeToInsert,
79
+ },
80
+ // Move cursor to the end of the inserted text
81
+ selection: { anchor: cursorPos + codeToInsert.length },
82
+ });
83
+ return true; // Prevent default Enter behavior
84
+ },
85
+ },
86
+ ]);
87
+ }
88
+ function findTypeByLeftBracketPos(jsonValue, leftBracketPos, idToRecordDef) {
89
+ let { expectedType } = jsonValue;
90
+ if (!expectedType) {
91
+ return null;
92
+ }
93
+ if (expectedType.kind === "optional") {
94
+ expectedType = expectedType.value;
95
+ }
96
+ switch (jsonValue.kind) {
97
+ case "array": {
98
+ if (expectedType.kind !== "array") {
99
+ return null;
100
+ }
101
+ if (leftBracketPos === jsonValue.firstToken.start) {
102
+ // We have a match
103
+ return expectedType;
104
+ }
105
+ // If no match, we can continue searching in the elements.
106
+ for (const element of jsonValue.values) {
107
+ if (leftBracketPos < element.firstToken.start) {
108
+ return null; // No need to continue (optimization)
109
+ }
110
+ const maybeResult = findTypeByLeftBracketPos(element, leftBracketPos, idToRecordDef);
111
+ if (maybeResult) {
112
+ return maybeResult;
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+ case "object": {
118
+ if (expectedType.kind === "primitive" &&
119
+ expectedType.value === "timestamp" &&
120
+ leftBracketPos === jsonValue.firstToken.start) {
121
+ return { kind: "timestamp" };
122
+ }
123
+ if (expectedType.kind !== "record") {
124
+ return null;
125
+ }
126
+ if (leftBracketPos === jsonValue.firstToken.start) {
127
+ // We have a match
128
+ return expectedType;
129
+ }
130
+ // If no match, we can continue searching in the values.
131
+ const recordDef = idToRecordDef[expectedType.value];
132
+ if (recordDef.kind === "struct") {
133
+ for (const keyValue of Object.values(jsonValue.keyValues)) {
134
+ const maybeResult = findTypeByLeftBracketPos(keyValue.value, leftBracketPos, idToRecordDef);
135
+ if (maybeResult) {
136
+ return maybeResult;
137
+ }
138
+ }
139
+ }
140
+ else {
141
+ const valueKv = jsonValue.keyValues["value"];
142
+ if (valueKv) {
143
+ const maybeResult = findTypeByLeftBracketPos(valueKv.value, leftBracketPos, idToRecordDef);
144
+ if (maybeResult) {
145
+ return maybeResult;
146
+ }
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+ case "literal": {
152
+ return null;
153
+ }
154
+ }
155
+ }
156
+ /** Returns the indentation at the line containing `position`. */
157
+ function getLineIndentation(text, position) {
158
+ if (position < 0 || position >= text.length) {
159
+ return "";
160
+ }
161
+ // Find the start of the line containing the position
162
+ let lineStart = position;
163
+ while (lineStart > 0 && text[lineStart - 1] !== "\n") {
164
+ lineStart--;
165
+ }
166
+ // Extract indentation (spaces at the beginning of the line)
167
+ let indentation = "";
168
+ let i = lineStart;
169
+ while (i < text.length && text[i] === " ") {
170
+ indentation += " ";
171
+ i++;
172
+ }
173
+ return indentation;
174
+ }
175
+ function indent(text, indentation) {
176
+ return text
177
+ .split("\n")
178
+ .map((line) => indentation + line)
179
+ .join("\n");
180
+ }
181
+ //# sourceMappingURL=enter_key_handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enter_key_handler.js","sourceRoot":"","sources":["../../src/codemirror/enter_key_handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAQnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAsB;IACpD,OAAO,MAAM,CAAC,EAAE,CAAC;QACf;YACE,GAAG,EAAE,OAAO;YACZ,GAAG,EAAE,CAAC,IAAgB,EAAW,EAAE;gBACjC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;gBACvB,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;gBAC5B,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;gBAE3B,kCAAkC;gBAClC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC;gBAE5B,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;gBACvE,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBAC/C,OAAO,KAAK,CAAC,CAAC,+BAA+B;gBAC/C,CAAC;gBAED,kCAAkC;gBAClC,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAChD,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;gBAC1C,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;oBACvB,OAAO,KAAK,CAAC;gBACf,CAAC;gBACD,MAAM,SAAS,GAAc,WAAW,CAAC,KAAK,CAAC;gBAE/C,MAAM,aAAa,GAAuC,EAAE,CAAC;gBAC7D,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACpC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;gBACpC,CAAC;gBACD,MAAM,IAAI,GAAG,wBAAwB,CACnC,SAAS,EACT,SAAS,GAAG,CAAC,EACb,aAAa,CACd,CAAC;gBACF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAClB,OAAO,KAAK,CAAC;gBACf,CAAC;gBAED,IAAI,YAAoB,CAAC;gBACzB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC1B,YAAY,GAAG,IAAI,CAAC,SAAS,CAC3B,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,EAChD,IAAI,EACJ,CAAC,CACF,CAAC;gBACJ,CAAC;qBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAClC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBAC5C,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;wBAChC,YAAY,GAAG,IAAI,CAAC,SAAS,CAC3B,gBAAgB,CAAC,IAAI,EAAE,aAAa,CAAC,EACrC,IAAI,EACJ,CAAC,CACF,CAAC;wBACF,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;4BAC1B,OAAO,KAAK,CAAC;wBACf,CAAC;wBACD,mEAAmE;wBACnE,sBAAsB;wBACtB,YAAY,GAAG,YAAY;6BACxB,KAAK,CAAC,IAAI,CAAC;6BACX,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;6BACZ,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;6BAChC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAChB,CAAC;yBAAM,CAAC;wBACN,OAAO;wBACP,YAAY,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC3D,CAAC;gBACH,CAAC;qBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBACrC,YAAY;wBACV,4DAA4D,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,GAAU,IAAI,CAAC;oBACtB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;gBACrB,CAAC;gBAED,oEAAoE;gBACpE,MAAM,eAAe,GAAG,kBAAkB,CACxC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EACpB,SAAS,GAAG,CAAC,CACd,CAAC;gBACF,YAAY,GAAG,MAAM,CAAC,YAAY,EAAE,IAAI,GAAG,eAAe,CAAC,CAAC;gBAE5D,YAAY,GAAG,KAAK,YAAY,KAAK,eAAe,EAAE,CAAC;gBAEvD,wBAAwB;gBACxB,IAAI,CAAC,QAAQ,CAAC;oBACZ,OAAO,EAAE;wBACP,IAAI,EAAE,SAAS;wBACf,EAAE,EAAE,SAAS;wBACb,MAAM,EAAE,YAAY;qBACrB;oBACD,8CAA8C;oBAC9C,SAAS,EAAE,EAAE,MAAM,EAAE,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE;iBACvD,CAAC,CAAC;gBAEH,OAAO,IAAI,CAAC,CAAC,iCAAiC;YAChD,CAAC;SACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,wBAAwB,CAC/B,SAAoB,EACpB,cAAsB,EACtB,aAAiD;IAEjD,IAAI,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,YAAY,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACrC,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC;IACpC,CAAC;IACD,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,YAAY,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAClC,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,cAAc,KAAK,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBAClD,kBAAkB;gBAClB,OAAO,YAAY,CAAC;YACtB,CAAC;YACD,0DAA0D;YAC1D,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;gBACvC,IAAI,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;oBAC9C,OAAO,IAAI,CAAC,CAAC,qCAAqC;gBACpD,CAAC;gBACD,MAAM,WAAW,GAAG,wBAAwB,CAC1C,OAAO,EACP,cAAc,EACd,aAAa,CACd,CAAC;gBACF,IAAI,WAAW,EAAE,CAAC;oBAChB,OAAO,WAAW,CAAC;gBACrB,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IACE,YAAY,CAAC,IAAI,KAAK,WAAW;gBACjC,YAAY,CAAC,KAAK,KAAK,WAAW;gBAClC,cAAc,KAAK,SAAS,CAAC,UAAU,CAAC,KAAK,EAC7C,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YAC/B,CAAC;YACD,IAAI,YAAY,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACnC,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,cAAc,KAAK,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBAClD,kBAAkB;gBAClB,OAAO,YAAY,CAAC;YACtB,CAAC;YACD,wDAAwD;YACxD,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACpD,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAChC,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC1D,MAAM,WAAW,GAAG,wBAAwB,CAC1C,QAAQ,CAAC,KAAK,EACd,cAAc,EACd,aAAa,CACd,CAAC;oBACF,IAAI,WAAW,EAAE,CAAC;wBAChB,OAAO,WAAW,CAAC;oBACrB,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBAC7C,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,WAAW,GAAG,wBAAwB,CAC1C,OAAO,CAAC,KAAK,EACb,cAAc,EACd,aAAa,CACd,CAAC;oBACF,IAAI,WAAW,EAAE,CAAC;wBAChB,OAAO,WAAW,CAAC;oBACrB,CAAC;gBACH,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED,iEAAiE;AACjE,SAAS,kBAAkB,CAAC,IAAY,EAAE,QAAgB;IACxD,IAAI,QAAQ,GAAG,CAAC,IAAI,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,qDAAqD;IACrD,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,OAAO,SAAS,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACrD,SAAS,EAAE,CAAC;IACd,CAAC;IAED,4DAA4D;IAC5D,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,CAAC,GAAG,SAAS,CAAC;IAClB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;QAC1C,WAAW,IAAI,GAAG,CAAC;QACnB,CAAC,EAAE,CAAC;IACN,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAS,MAAM,CAAC,IAAY,EAAE,WAAmB;IAC/C,OAAO,IAAI;SACR,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,GAAG,IAAI,CAAC;SACjC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC","sourcesContent":["import { Extension } from \"@codemirror/state\";\nimport { EditorView, keymap } from \"@codemirror/view\";\nimport { makeJsonTemplate } from \"../json/to_json\";\nimport {\n ArrayTypeSignature,\n JsonValue,\n RecordDefinition,\n RecordTypeSignature,\n TypeDefinition,\n} from \"../json/types\";\nimport { ensureJsonState } from \"./json_state\";\n\n/**\n * Triggered when the Enter key is pressed between 2 consecutive curly braces or\n * square brackets.\n */\nexport function enterKeyHandler(schema: TypeDefinition): Extension {\n return keymap.of([\n {\n key: \"Enter\",\n run: (view: EditorView): boolean => {\n const { state } = view;\n const { selection } = state;\n const { main } = selection;\n\n // Get the current cursor position\n const cursorPos = main.head;\n\n const textAround = state.doc.sliceString(cursorPos - 1, cursorPos + 1);\n if (textAround !== \"{}\" && textAround !== \"[]\") {\n return false; // Allow default Enter behavior\n }\n\n // Ensure JSON state is up-to-date\n const jsonState = ensureJsonState(view, schema);\n const parseResult = jsonState.parseResult;\n if (!parseResult.value) {\n return false;\n }\n const jsonValue: JsonValue = parseResult.value;\n\n const idToRecordDef: { [id: string]: RecordDefinition } = {};\n for (const record of schema.records) {\n idToRecordDef[record.id] = record;\n }\n const type = findTypeByLeftBracketPos(\n jsonValue,\n cursorPos - 1,\n idToRecordDef,\n );\n if (type === null) {\n return false;\n }\n\n let codeToInsert: string;\n if (type.kind === \"array\") {\n codeToInsert = JSON.stringify(\n makeJsonTemplate(type.value.item, idToRecordDef),\n null,\n 2,\n );\n } else if (type.kind === \"record\") {\n const recordDef = idToRecordDef[type.value];\n if (recordDef.kind === \"struct\") {\n codeToInsert = JSON.stringify(\n makeJsonTemplate(type, idToRecordDef),\n null,\n 2,\n );\n if (codeToInsert === \"{}\") {\n return false;\n }\n // Remove the curly brackets at the start and end of the string and\n // unindent each line.\n codeToInsert = codeToInsert\n .split(\"\\n\")\n .slice(1, -1)\n .map((line) => line.substring(2))\n .join(\"\\n\");\n } else {\n // Enum\n codeToInsert = ['\"kind\": \"\",', '\"value\": {}'].join(\"\\n\");\n }\n } else if (type.kind === \"timestamp\") {\n codeToInsert =\n '\"unix_millis\": 0,\\n\"formatted\": \"1970-01-01T00:00:00.000Z\"';\n } else {\n const _: never = type;\n throw new Error(_);\n }\n\n // Indent the code to insert based on the current line's indentation\n const lineIndentation = getLineIndentation(\n state.doc.toString(),\n cursorPos - 1,\n );\n codeToInsert = indent(codeToInsert, \" \" + lineIndentation);\n\n codeToInsert = `\\n${codeToInsert}\\n${lineIndentation}`;\n\n // Apply the transaction\n view.dispatch({\n changes: {\n from: cursorPos,\n to: cursorPos,\n insert: codeToInsert,\n },\n // Move cursor to the end of the inserted text\n selection: { anchor: cursorPos + codeToInsert.length },\n });\n\n return true; // Prevent default Enter behavior\n },\n },\n ]);\n}\n\nfunction findTypeByLeftBracketPos(\n jsonValue: JsonValue,\n leftBracketPos: number,\n idToRecordDef: { [id: string]: RecordDefinition },\n): ArrayTypeSignature | RecordTypeSignature | { kind: \"timestamp\" } | null {\n let { expectedType } = jsonValue;\n if (!expectedType) {\n return null;\n }\n if (expectedType.kind === \"optional\") {\n expectedType = expectedType.value;\n }\n switch (jsonValue.kind) {\n case \"array\": {\n if (expectedType.kind !== \"array\") {\n return null;\n }\n if (leftBracketPos === jsonValue.firstToken.start) {\n // We have a match\n return expectedType;\n }\n // If no match, we can continue searching in the elements.\n for (const element of jsonValue.values) {\n if (leftBracketPos < element.firstToken.start) {\n return null; // No need to continue (optimization)\n }\n const maybeResult = findTypeByLeftBracketPos(\n element,\n leftBracketPos,\n idToRecordDef,\n );\n if (maybeResult) {\n return maybeResult;\n }\n }\n return null;\n }\n case \"object\": {\n if (\n expectedType.kind === \"primitive\" &&\n expectedType.value === \"timestamp\" &&\n leftBracketPos === jsonValue.firstToken.start\n ) {\n return { kind: \"timestamp\" };\n }\n if (expectedType.kind !== \"record\") {\n return null;\n }\n if (leftBracketPos === jsonValue.firstToken.start) {\n // We have a match\n return expectedType;\n }\n // If no match, we can continue searching in the values.\n const recordDef = idToRecordDef[expectedType.value];\n if (recordDef.kind === \"struct\") {\n for (const keyValue of Object.values(jsonValue.keyValues)) {\n const maybeResult = findTypeByLeftBracketPos(\n keyValue.value,\n leftBracketPos,\n idToRecordDef,\n );\n if (maybeResult) {\n return maybeResult;\n }\n }\n } else {\n const valueKv = jsonValue.keyValues[\"value\"];\n if (valueKv) {\n const maybeResult = findTypeByLeftBracketPos(\n valueKv.value,\n leftBracketPos,\n idToRecordDef,\n );\n if (maybeResult) {\n return maybeResult;\n }\n }\n }\n return null;\n }\n case \"literal\": {\n return null;\n }\n }\n}\n\n/** Returns the indentation at the line containing `position`. */\nfunction getLineIndentation(text: string, position: number): string {\n if (position < 0 || position >= text.length) {\n return \"\";\n }\n\n // Find the start of the line containing the position\n let lineStart = position;\n while (lineStart > 0 && text[lineStart - 1] !== \"\\n\") {\n lineStart--;\n }\n\n // Extract indentation (spaces at the beginning of the line)\n let indentation = \"\";\n let i = lineStart;\n while (i < text.length && text[i] === \" \") {\n indentation += \" \";\n i++;\n }\n\n return indentation;\n}\n\nfunction indent(text: string, indentation: string): string {\n return text\n .split(\"\\n\")\n .map((line) => indentation + line)\n .join(\"\\n\");\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import { CompletionContext, CompletionResult } from "@codemirror/autocomplete";
2
+ import { TypeDefinition } from "../json/types";
3
+ export declare function jsonCompletion(schema: TypeDefinition): (context: CompletionContext) => CompletionResult | null;
4
+ //# sourceMappingURL=json_completion.d.ts.map