react-codemirror-editor 0.1.0 → 0.2.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.
- package/README.md +229 -44
- package/dist/core/diagnostics/json/index.d.ts.map +1 -0
- package/dist/core/diagnostics/{jsonDiagnostics.d.ts → json/jsonDiagnostics.d.ts} +1 -1
- package/dist/core/diagnostics/json/jsonDiagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics/json/jsonDiagnostics.js +32 -0
- package/dist/core/diagnostics/json/jsonLinter.d.ts +4 -0
- package/dist/core/diagnostics/json/jsonLinter.d.ts.map +1 -0
- package/dist/core/diagnostics/json/jsonLinter.js +42 -0
- package/dist/core/diagnostics/json/jsonValidation.d.ts +4 -0
- package/dist/core/diagnostics/json/jsonValidation.d.ts.map +1 -0
- package/dist/core/diagnostics/json/jsonValidation.js +8 -0
- package/dist/core/diagnostics/json/safeJsonCompletion.d.ts +3 -0
- package/dist/core/diagnostics/json/safeJsonCompletion.d.ts.map +1 -0
- package/dist/core/diagnostics/json/safeJsonCompletion.js +8 -0
- package/dist/core/editor/createEditor.d.ts +1 -1
- package/dist/core/editor/createEditor.d.ts.map +1 -1
- package/dist/core/editor/createEditor.js +3 -2
- package/dist/core/editor/editorController.d.ts.map +1 -1
- package/dist/core/editor/editorController.js +19 -1
- package/dist/core/extensions/fold.d.ts.map +1 -1
- package/dist/core/extensions/fold.js +14 -2
- package/dist/core/extensions/index.d.ts +1 -0
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -0
- package/dist/core/extensions/search.d.ts +11 -0
- package/dist/core/extensions/search.d.ts.map +1 -0
- package/dist/core/extensions/search.js +54 -0
- package/dist/core/languages/buildExtensions.js +1 -1
- package/dist/types/editor.d.ts +17 -0
- package/dist/types/editor.d.ts.map +1 -1
- package/dist/ui/CodeEditor.d.ts.map +1 -1
- package/dist/ui/CodeEditor.js +2 -2
- package/dist/ui/EditorContainer.d.ts +1 -1
- package/dist/ui/EditorContainer.d.ts.map +1 -1
- package/dist/ui/EditorContainer.js +2 -1
- package/package.json +8 -6
- package/dist/core/diagnostics/index.d.ts.map +0 -1
- package/dist/core/diagnostics/jsonDiagnostics.d.ts.map +0 -1
- package/dist/core/diagnostics/jsonDiagnostics.js +0 -78
- /package/dist/core/diagnostics/{index.d.ts → json/index.d.ts} +0 -0
- /package/dist/core/diagnostics/{index.js → json/index.js} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# React Code Editor
|
|
2
2
|
|
|
3
|
-
A modern, extensible **CodeMirror 6–based React code editor**
|
|
3
|
+
A modern, extensible **CodeMirror 6–based React code editor** featuring first-class TypeScript support, language-aware configuration, optional diagnostics, and search/validation support.
|
|
4
4
|
|
|
5
5
|
This library is designed to scale from a simple embedded editor to a **multi-language, schema-aware editing platform**.
|
|
6
6
|
|
|
@@ -14,6 +14,8 @@ This library is designed to scale from a simple embedded editor to a **multi-lan
|
|
|
14
14
|
- Optional diagnostics, completion, and hover
|
|
15
15
|
- JSON Schema validation support
|
|
16
16
|
- Light & dark themes
|
|
17
|
+
- Search & Replace support
|
|
18
|
+
- Validation state callback
|
|
17
19
|
- Designed for future multi-language support
|
|
18
20
|
|
|
19
21
|
---
|
|
@@ -32,10 +34,10 @@ npm install react-code-editor
|
|
|
32
34
|
npm install react react-dom
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
### JSON language support (optional but recommended)
|
|
37
|
+
### JSON language support (optional, but recommended)
|
|
36
38
|
|
|
37
39
|
```bash
|
|
38
|
-
npm install @codemirror/lang-json
|
|
40
|
+
npm install @codemirror/lang-json codemirror-json-schema ajv
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
> `ajv` is used internally by `codemirror-json-schema` for JSON Schema validation.
|
|
@@ -48,7 +50,7 @@ npm install @codemirror/lang-json codemirrorirror-json-schema ajv
|
|
|
48
50
|
import { CodeEditor } from 'react-code-editor';
|
|
49
51
|
|
|
50
52
|
export function Example() {
|
|
51
|
-
|
|
53
|
+
return <CodeEditor language="json" defaultValue="{}" />;
|
|
52
54
|
}
|
|
53
55
|
```
|
|
54
56
|
|
|
@@ -61,10 +63,7 @@ This creates an **uncontrolled JSON editor** with default configuration.
|
|
|
61
63
|
### Uncontrolled Editor
|
|
62
64
|
|
|
63
65
|
```tsx
|
|
64
|
-
<CodeEditor
|
|
65
|
-
language="json"
|
|
66
|
-
defaultValue='{ "name": "John" }'
|
|
67
|
-
/>
|
|
66
|
+
<CodeEditor language="json" defaultValue='{ "name": "John" }' />
|
|
68
67
|
```
|
|
69
68
|
|
|
70
69
|
### Controlled Editor
|
|
@@ -72,14 +71,144 @@ This creates an **uncontrolled JSON editor** with default configuration.
|
|
|
72
71
|
```tsx
|
|
73
72
|
const [value, setValue] = useState('{}');
|
|
74
73
|
|
|
74
|
+
<CodeEditor language="json" value={value} onChange={setValue} />;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
> ⚠️ Do not pass both `value` and `defaultValue`.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🎮 Editor Controller API
|
|
82
|
+
|
|
83
|
+
The editor exposes a controller API that allows you to trigger actions programmatically.
|
|
84
|
+
|
|
85
|
+
### ⚠️ Important design choice
|
|
86
|
+
|
|
87
|
+
This library does **not** impose formatting logic.
|
|
88
|
+
Instead, you **pass a callback function** that receives the current editor value and returns the formatted result.
|
|
89
|
+
|
|
90
|
+
This keeps the editor **language-agnostic** and flexible.
|
|
91
|
+
|
|
92
|
+
### Available Controller Actions
|
|
93
|
+
|
|
94
|
+
- `copy()`
|
|
95
|
+
- `format(formatter)`
|
|
96
|
+
- `foldAll()`
|
|
97
|
+
- `unfoldAll()`
|
|
98
|
+
- `openSearch()`
|
|
99
|
+
- `closeSearch()`
|
|
100
|
+
- `findNext()`
|
|
101
|
+
- `findPrev()`
|
|
102
|
+
- `replace(replacement: string)`
|
|
103
|
+
- `replaceAll(replacement: string)`
|
|
104
|
+
|
|
105
|
+
### 🧠 Format API (Callback-Based)
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
format(formatter: (value: string) => string): void
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- The editor passes the **current content**
|
|
112
|
+
- Your formatter returns the **new formatted content**
|
|
113
|
+
- The editor updates itself with the returned value
|
|
114
|
+
|
|
115
|
+
### Example
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { useRef } from 'react';
|
|
119
|
+
import type { EditorController } from 'react-codemirror-editor';
|
|
120
|
+
|
|
121
|
+
const controllerRef = useRef<EditorController | null>(null);
|
|
122
|
+
|
|
75
123
|
<CodeEditor
|
|
76
124
|
language="json"
|
|
77
|
-
|
|
78
|
-
onChange={setValue}
|
|
125
|
+
controllerRef={controllerRef}
|
|
79
126
|
/>;
|
|
127
|
+
|
|
128
|
+
function formatJson(value: string) {
|
|
129
|
+
try {
|
|
130
|
+
return JSON.stringify(JSON.parse(value), null, 2);
|
|
131
|
+
} catch {
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
<button onClick={() => controllerRef.current?.format(formatJson)}>Format</button>
|
|
137
|
+
<button onClick={() => controllerRef.current?.copy()}>Copy</button>
|
|
138
|
+
<button onClick={() => controllerRef.current?.foldAll()}>Fold All</button>
|
|
139
|
+
<button onClick={() => controllerRef.current?.unfoldAll()}>Unfold All</button>
|
|
140
|
+
<button onClick={() => controllerRef.current?.openSearch()}>Search</button>
|
|
141
|
+
<button onClick={() => controllerRef.current?.closeSearch()}>Close Search</button>
|
|
80
142
|
```
|
|
81
143
|
|
|
82
|
-
|
|
144
|
+
- Works for **any language**
|
|
145
|
+
- Supports **custom formatter functions**
|
|
146
|
+
- Search UI is **optional** and can be enabled via props
|
|
147
|
+
|
|
148
|
+
### Example: Prettier Integration (Concept)
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
controllerRef.current?.format((code) =>
|
|
152
|
+
prettier.format(code, { parser: 'json' }),
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 📋 Why Callback-Based Formatting?
|
|
159
|
+
|
|
160
|
+
- Keeps core editor **small**
|
|
161
|
+
- Avoids hard dependency on Prettier
|
|
162
|
+
- Supports **any language**
|
|
163
|
+
- Lets consumers decide formatting rules
|
|
164
|
+
|
|
165
|
+
This is a library-level design decision, not a limitation.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 🔍 Search & Replace
|
|
170
|
+
|
|
171
|
+
The editor includes **search & replace functionality** via a controller API:
|
|
172
|
+
|
|
173
|
+
- `openSearch()` – Opens the search panel
|
|
174
|
+
- `closeSearch()` – Closes the search panel
|
|
175
|
+
- `findNext()` – Finds the next match
|
|
176
|
+
- `findPrev()` – Finds the previous match
|
|
177
|
+
- `replace(replacement: string)` – Replaces the current match
|
|
178
|
+
- `replaceAll(replacement: string)` – Replaces all matches
|
|
179
|
+
|
|
180
|
+
You can pass **search configuration**:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<CodeEditor
|
|
184
|
+
language="json"
|
|
185
|
+
searchOptions={{
|
|
186
|
+
top: true,
|
|
187
|
+
caseSensitive: false,
|
|
188
|
+
}}
|
|
189
|
+
/>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## ✅ JSON Validation State
|
|
195
|
+
|
|
196
|
+
You can track JSON validity and react to changes in real-time via `onValidationChange`:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
<CodeEditor
|
|
200
|
+
language="json"
|
|
201
|
+
languageOptions={{
|
|
202
|
+
json: {
|
|
203
|
+
schema: myJsonSchema,
|
|
204
|
+
onValidationChange: (isValid) => console.log('Valid:', isValid),
|
|
205
|
+
},
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- `isValid` is `true` if there are no syntax or schema errors
|
|
211
|
+
- Useful for enabling/disabling Save buttons or warnings in your UI
|
|
83
212
|
|
|
84
213
|
---
|
|
85
214
|
|
|
@@ -88,9 +217,11 @@ const [value, setValue] = useState('{}');
|
|
|
88
217
|
Languages are enabled explicitly using the `language` prop.
|
|
89
218
|
|
|
90
219
|
### Currently supported
|
|
220
|
+
|
|
91
221
|
- **JSON**
|
|
92
222
|
|
|
93
|
-
|
|
223
|
+
Future support for:
|
|
224
|
+
|
|
94
225
|
- JavaScript
|
|
95
226
|
- TypeScript
|
|
96
227
|
- Python
|
|
@@ -102,32 +233,31 @@ The architecture is designed to support additional languages such as:
|
|
|
102
233
|
|
|
103
234
|
Language-specific behavior is configured via `languageOptions`.
|
|
104
235
|
|
|
105
|
-
### JSON Configuration Example
|
|
106
|
-
|
|
107
236
|
```tsx
|
|
108
237
|
<CodeEditor
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
238
|
+
language="json"
|
|
239
|
+
languageOptions={{
|
|
240
|
+
json: {
|
|
241
|
+
schema: myJsonSchema,
|
|
242
|
+
diagnostics: true,
|
|
243
|
+
completion: true,
|
|
244
|
+
hover: true,
|
|
245
|
+
},
|
|
246
|
+
}}
|
|
118
247
|
/>
|
|
119
248
|
```
|
|
120
249
|
|
|
121
|
-
---
|
|
122
|
-
|
|
123
250
|
### JSON Options
|
|
124
251
|
|
|
125
|
-
| Option
|
|
126
|
-
|
|
127
|
-
| `schema` | JSON Schema
|
|
128
|
-
| `diagnostics`
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
252
|
+
| Option | Type | Default | Description |
|
|
253
|
+
| -------------------- | ---------------------------- | ------------------------- | ------------------------------------------------------ |
|
|
254
|
+
| `schema` | `object` | `undefined` | JSON Schema used for validation, completion, and hover |
|
|
255
|
+
| `diagnostics` | `boolean` | `true` | Enables JSON syntax diagnostics |
|
|
256
|
+
| `gutter` | `boolean` | `true` | Shows the diagnostics gutter (error markers) |
|
|
257
|
+
| `schemaLint` | `boolean` | `true if schema provided` | Enables schema-based validation |
|
|
258
|
+
| `hover` | `boolean` | `true if schema provided` | Enables hover tooltips from schema |
|
|
259
|
+
| `autocomplete` | `boolean` | `true if schema provided` | Enables schema-based autocompletion |
|
|
260
|
+
| `onValidationChange` | `(isValid: boolean) => void` | `undefined` | Callback called whenever JSON validity changes |
|
|
131
261
|
|
|
132
262
|
> If no schema is provided, the editor still works normally with **syntax diagnostics only**.
|
|
133
263
|
|
|
@@ -137,11 +267,12 @@ Language-specific behavior is configured via `languageOptions`.
|
|
|
137
267
|
|
|
138
268
|
Diagnostics are **configurable per language**.
|
|
139
269
|
|
|
140
|
-
### JSON diagnostics include
|
|
270
|
+
### JSON diagnostics include
|
|
271
|
+
|
|
141
272
|
- Syntax errors
|
|
142
273
|
- Schema validation errors (when schema is provided)
|
|
143
274
|
|
|
144
|
-
|
|
275
|
+
Disable diagnostics:
|
|
145
276
|
|
|
146
277
|
```tsx
|
|
147
278
|
languageOptions={{
|
|
@@ -156,25 +287,79 @@ languageOptions={{
|
|
|
156
287
|
## 🔒 Read-Only Mode
|
|
157
288
|
|
|
158
289
|
```tsx
|
|
159
|
-
<CodeEditor
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
290
|
+
<CodeEditor language="json" value={json} readOnly={true} />
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Notes:**
|
|
294
|
+
|
|
295
|
+
- `readOnly` must be a boolean
|
|
296
|
+
- Default is `false`
|
|
297
|
+
- When enabled, the editor is non-editable but remains selectable and scrollable
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## 📐 Layout & Sizing
|
|
302
|
+
|
|
303
|
+
By default, CodeMirror sizes itself based on content height.
|
|
304
|
+
This can result in a single-line editor when the value contains only one line.
|
|
305
|
+
|
|
306
|
+
The editor is designed to expand and fill its container.
|
|
307
|
+
To ensure a consistent height, define a height or `min-height` via CSS.
|
|
308
|
+
|
|
309
|
+
```css
|
|
310
|
+
.cm-editor-container {
|
|
311
|
+
min-height: 200px;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.cm-editor-container,
|
|
315
|
+
.cm-editor-container .cm-editor {
|
|
316
|
+
width: 100%;
|
|
317
|
+
height: 100%;
|
|
318
|
+
}
|
|
164
319
|
```
|
|
165
320
|
|
|
321
|
+
**Notes:**
|
|
322
|
+
|
|
323
|
+
- The editor always fills the container height
|
|
324
|
+
- Padding, borders, and background should be applied to the container
|
|
325
|
+
- Provides full control over responsive layouts
|
|
326
|
+
|
|
166
327
|
---
|
|
167
328
|
|
|
168
329
|
## 🎨 Theming
|
|
169
330
|
|
|
170
331
|
```tsx
|
|
171
|
-
<CodeEditor
|
|
172
|
-
language="json"
|
|
173
|
-
theme="dark"
|
|
174
|
-
/>
|
|
332
|
+
<CodeEditor language="json" theme="dark" />
|
|
175
333
|
```
|
|
176
334
|
|
|
177
|
-
Both **light and dark themes** are supported, with multiple variants
|
|
335
|
+
Both **light and dark themes** are supported, each with multiple variants.
|
|
336
|
+
|
|
337
|
+
Available themes:
|
|
338
|
+
|
|
339
|
+
Light themes:
|
|
340
|
+
|
|
341
|
+
- light
|
|
342
|
+
- ayu_light
|
|
343
|
+
- clouds_light
|
|
344
|
+
- espresso_light
|
|
345
|
+
- noctis_lilac_light
|
|
346
|
+
- rose_pine_dawn_light
|
|
347
|
+
- smoothy_light
|
|
348
|
+
- tomorrow_light
|
|
349
|
+
|
|
350
|
+
Dark themes:
|
|
351
|
+
|
|
352
|
+
- dark
|
|
353
|
+
- barf_dark
|
|
354
|
+
- cobalt_dark
|
|
355
|
+
- cool_glow_dark
|
|
356
|
+
- dracula_dark
|
|
357
|
+
|
|
358
|
+
Theme names are type-safe via the exported ThemeName union.
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
import type { ThemeName } from 'react-codemirror-editor';
|
|
362
|
+
```
|
|
178
363
|
|
|
179
364
|
---
|
|
180
365
|
|
|
@@ -182,7 +367,7 @@ Both **light and dark themes** are supported, with multiple variants included.
|
|
|
182
367
|
|
|
183
368
|
- Built on **CodeMirror 6**
|
|
184
369
|
- Language features are isolated and composable
|
|
185
|
-
- Diagnostics, completion, and
|
|
370
|
+
- Diagnostics, completion, hover, and search are opt-in
|
|
186
371
|
- Clean separation between core editor, languages, and UI
|
|
187
372
|
- Designed for long-term multi-language expansion
|
|
188
373
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/diagnostics/json/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Extension } from '@codemirror/state';
|
|
2
|
-
import { JsonEditorConfig } from '
|
|
2
|
+
import { JsonEditorConfig } from '../../../types';
|
|
3
3
|
export declare const jsonDiagnosticsExtension: (options?: JsonEditorConfig) => Extension;
|
|
4
4
|
//# sourceMappingURL=jsonDiagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonDiagnostics.d.ts","sourceRoot":"","sources":["../../../../src/core/diagnostics/json/jsonDiagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAa9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,eAAO,MAAM,wBAAwB,aACxB,gBAAgB,KAC1B,SA4CF,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { hoverTooltip } from '@codemirror/view';
|
|
2
|
+
import { linter, lintGutter } from '@codemirror/lint';
|
|
3
|
+
import { autocompletion } from '@codemirror/autocomplete';
|
|
4
|
+
import { jsonSchemaLinter, jsonSchemaHover, stateExtensions, } from 'codemirror-json-schema';
|
|
5
|
+
import { validation } from './jsonValidation';
|
|
6
|
+
import { jsonLinter } from './jsonLinter';
|
|
7
|
+
import { safeJsonCompletion } from './safeJsonCompletion';
|
|
8
|
+
export const jsonDiagnosticsExtension = (options = {}) => {
|
|
9
|
+
const { diagnostics = true, gutter = true, schema, schemaLint = !!schema, hover = !!schema, autocomplete = !!schema, onValidationChange, } = options;
|
|
10
|
+
const extensions = [];
|
|
11
|
+
if (diagnostics) {
|
|
12
|
+
extensions.push(linter(validation(jsonLinter, onValidationChange)));
|
|
13
|
+
if (gutter) {
|
|
14
|
+
extensions.push(lintGutter());
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (schema) {
|
|
18
|
+
extensions.push(stateExtensions(schema));
|
|
19
|
+
if (schemaLint) {
|
|
20
|
+
extensions.push(linter(validation(jsonSchemaLinter(schema), onValidationChange)));
|
|
21
|
+
}
|
|
22
|
+
if (hover) {
|
|
23
|
+
extensions.push(hoverTooltip(jsonSchemaHover(schema)));
|
|
24
|
+
}
|
|
25
|
+
if (autocomplete) {
|
|
26
|
+
extensions.push(autocompletion({
|
|
27
|
+
override: [safeJsonCompletion(schema)],
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return extensions;
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonLinter.d.ts","sourceRoot":"","sources":["../../../../src/core/diagnostics/json/jsonLinter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA+B9C,eAAO,MAAM,UAAU,SAAU,UAAU,KAAG,UAAU,EAsBvD,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { syntaxTree } from '@codemirror/language';
|
|
2
|
+
const getErrorMessage = (view, from) => {
|
|
3
|
+
const doc = view.state.doc;
|
|
4
|
+
const charBefore = from > 0 ? doc.sliceString(from - 1, from) : '';
|
|
5
|
+
const charAfter = doc.sliceString(from, from + 1);
|
|
6
|
+
if (charBefore === ',') {
|
|
7
|
+
return 'Trailing comma is not allowed in JSON';
|
|
8
|
+
}
|
|
9
|
+
if (charAfter === '}' || charAfter === ']') {
|
|
10
|
+
return 'Missing value before closing bracket';
|
|
11
|
+
}
|
|
12
|
+
if (charAfter === ':') {
|
|
13
|
+
return 'Missing value after ":"';
|
|
14
|
+
}
|
|
15
|
+
if (charBefore === ':') {
|
|
16
|
+
return 'Missing value after ":"';
|
|
17
|
+
}
|
|
18
|
+
if (!charAfter) {
|
|
19
|
+
return 'Unexpected end of JSON input';
|
|
20
|
+
}
|
|
21
|
+
return 'Invalid JSON syntax';
|
|
22
|
+
};
|
|
23
|
+
export const jsonLinter = (view) => {
|
|
24
|
+
const diagnostics = [];
|
|
25
|
+
const tree = syntaxTree(view.state);
|
|
26
|
+
const docLength = view.state.doc.length;
|
|
27
|
+
tree.iterate({
|
|
28
|
+
enter(node) {
|
|
29
|
+
if (!node.type.isError)
|
|
30
|
+
return;
|
|
31
|
+
const from = node.from;
|
|
32
|
+
const to = Math.min(node.to, docLength);
|
|
33
|
+
diagnostics.push({
|
|
34
|
+
from,
|
|
35
|
+
to: from === to ? from + 1 : to,
|
|
36
|
+
severity: 'error',
|
|
37
|
+
message: getErrorMessage(view, from),
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return diagnostics;
|
|
42
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { EditorView } from '@codemirror/view';
|
|
2
|
+
import { Diagnostic } from '@codemirror/lint';
|
|
3
|
+
export declare const validation: (linterFn: (view: EditorView) => Diagnostic[], onValidationChange?: (isValid: boolean) => void) => (view: EditorView) => Diagnostic[];
|
|
4
|
+
//# sourceMappingURL=jsonValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonValidation.d.ts","sourceRoot":"","sources":["../../../../src/core/diagnostics/json/jsonValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,eAAO,MAAM,UAAU,aAEL,CAAC,IAAI,EAAE,UAAU,KAAK,UAAU,EAAE,uBACvB,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,YAE5C,UAAU,KAAG,UAAU,EAS7B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const validation = (linterFn, onValidationChange) => (view) => {
|
|
2
|
+
const diagnostics = linterFn(view);
|
|
3
|
+
if (onValidationChange) {
|
|
4
|
+
const isValid = diagnostics.every((d) => d.severity !== 'error');
|
|
5
|
+
onValidationChange(isValid);
|
|
6
|
+
}
|
|
7
|
+
return diagnostics;
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safeJsonCompletion.d.ts","sourceRoot":"","sources":["../../../../src/core/diagnostics/json/safeJsonCompletion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG/E,eAAO,MAAM,kBAAkB,WAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAG7C,iBAAiB,KAAG,gBAAgB,GAAG,IAIvD,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { EditorView } from '@codemirror/view';
|
|
2
2
|
import { CreateEditorOptions } from '../../types';
|
|
3
|
-
export declare const createEditor: ({ value, parent, theme, readOnly, language, languageOptions, onChange, }: CreateEditorOptions) => EditorView;
|
|
3
|
+
export declare const createEditor: ({ value, parent, theme, readOnly, language, languageOptions, search, onChange, }: CreateEditorOptions) => EditorView;
|
|
4
4
|
//# sourceMappingURL=createEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createEditor.d.ts","sourceRoot":"","sources":["../../../src/core/editor/createEditor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"createEditor.d.ts","sourceRoot":"","sources":["../../../src/core/editor/createEditor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,YAAY,qFAStB,mBAAmB,eAqBrB,CAAC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { EditorState } from '@codemirror/state';
|
|
2
2
|
import { EditorView } from '@codemirror/view';
|
|
3
3
|
import { basicSetup } from 'codemirror';
|
|
4
|
-
import { readOnlyExtension } from '../extensions';
|
|
4
|
+
import { readOnlyExtension, searchExtensions } from '../extensions';
|
|
5
5
|
import { buildLanguageExtensions } from '../languages';
|
|
6
6
|
import { getThemeExtension } from '../themes';
|
|
7
|
-
export const createEditor = ({ value, parent, theme, readOnly = false, language, languageOptions, onChange, }) => {
|
|
7
|
+
export const createEditor = ({ value, parent, theme, readOnly = false, language, languageOptions, search, onChange, }) => {
|
|
8
8
|
const state = EditorState.create({
|
|
9
9
|
doc: value,
|
|
10
10
|
extensions: [
|
|
@@ -12,6 +12,7 @@ export const createEditor = ({ value, parent, theme, readOnly = false, language,
|
|
|
12
12
|
...buildLanguageExtensions(language, languageOptions),
|
|
13
13
|
getThemeExtension(theme),
|
|
14
14
|
readOnlyExtension(readOnly),
|
|
15
|
+
searchExtensions(search),
|
|
15
16
|
EditorView.updateListener.of((update) => {
|
|
16
17
|
if (update.docChanged) {
|
|
17
18
|
onChange?.(update.state.doc.toString());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editorController.d.ts","sourceRoot":"","sources":["../../../src/core/editor/editorController.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"editorController.d.ts","sourceRoot":"","sources":["../../../src/core/editor/editorController.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAgB,sBAAsB,IAAI,gBAAgB,CAyCzD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copyToClipboard, foldAllCode, unfoldAllCode, formatCode, } from '../extensions';
|
|
1
|
+
import { copyToClipboard, foldAllCode, unfoldAllCode, formatCode, openSearch, closeSearch, replace, replaceAllOccurrences, searchNext, searchPrevious, } from '../extensions';
|
|
2
2
|
export function createEditorController() {
|
|
3
3
|
let view = null;
|
|
4
4
|
return {
|
|
@@ -20,5 +20,23 @@ export function createEditorController() {
|
|
|
20
20
|
format(formatter) {
|
|
21
21
|
return formatCode(view, formatter);
|
|
22
22
|
},
|
|
23
|
+
openSearch() {
|
|
24
|
+
openSearch(view);
|
|
25
|
+
},
|
|
26
|
+
closeSearch() {
|
|
27
|
+
closeSearch(view);
|
|
28
|
+
},
|
|
29
|
+
searchNext() {
|
|
30
|
+
searchNext(view);
|
|
31
|
+
},
|
|
32
|
+
searchPrevious() {
|
|
33
|
+
searchPrevious(view);
|
|
34
|
+
},
|
|
35
|
+
replace() {
|
|
36
|
+
replace(view);
|
|
37
|
+
},
|
|
38
|
+
replaceAll() {
|
|
39
|
+
replaceAllOccurrences(view);
|
|
40
|
+
},
|
|
23
41
|
};
|
|
24
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/fold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/fold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AASnD,eAAO,MAAM,WAAW,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,YAmB9D,CAAC;AAEF,eAAO,MAAM,aAAa,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,YAIhE,CAAC"}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { syntaxTree, foldable, foldEffect, unfoldAll, } from '@codemirror/language';
|
|
2
2
|
export const foldAllCode = (view) => {
|
|
3
3
|
if (!view)
|
|
4
4
|
return false;
|
|
5
|
-
|
|
5
|
+
const effects = [];
|
|
6
|
+
syntaxTree(view.state).iterate({
|
|
7
|
+
enter(node) {
|
|
8
|
+
const range = foldable(view.state, node.from, node.to);
|
|
9
|
+
if (range) {
|
|
10
|
+
effects.push(foldEffect.of(range));
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
if (!effects.length)
|
|
15
|
+
return false;
|
|
16
|
+
view.dispatch({ effects });
|
|
17
|
+
return true;
|
|
6
18
|
};
|
|
7
19
|
export const unfoldAllCode = (view) => {
|
|
8
20
|
if (!view)
|
|
@@ -2,4 +2,5 @@ export { copyToClipboard } from './copy';
|
|
|
2
2
|
export { foldAllCode, unfoldAllCode } from './fold';
|
|
3
3
|
export { formatCode } from './format';
|
|
4
4
|
export { readOnlyExtension } from './readOnly';
|
|
5
|
+
export { openSearch, closeSearch, searchExtensions, searchNext, searchPrevious, replace, replaceAllOccurrences, } from './search';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EACH,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,OAAO,EACP,qBAAqB,GACxB,MAAM,UAAU,CAAC"}
|
|
@@ -2,3 +2,4 @@ export { copyToClipboard } from './copy';
|
|
|
2
2
|
export { foldAllCode, unfoldAllCode } from './fold';
|
|
3
3
|
export { formatCode } from './format';
|
|
4
4
|
export { readOnlyExtension } from './readOnly';
|
|
5
|
+
export { openSearch, closeSearch, searchExtensions, searchNext, searchPrevious, replace, replaceAllOccurrences, } from './search';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EditorView } from '@codemirror/view';
|
|
2
|
+
import { Extension } from '@codemirror/state';
|
|
3
|
+
import { SearchOptions } from '../../types';
|
|
4
|
+
export declare const openSearch: (view: EditorView | null | undefined) => void;
|
|
5
|
+
export declare const closeSearch: (view: EditorView | null | undefined) => void;
|
|
6
|
+
export declare const searchNext: (view: EditorView | null | undefined) => void;
|
|
7
|
+
export declare const searchPrevious: (view: EditorView | null | undefined) => void;
|
|
8
|
+
export declare const replace: (view: EditorView | null | undefined) => void;
|
|
9
|
+
export declare const replaceAllOccurrences: (view: EditorView | null | undefined) => void;
|
|
10
|
+
export declare const searchExtensions: (searchConfig?: boolean | SearchOptions) => Extension;
|
|
11
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAUnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAO,MAAM,UAAU,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,SAI7D,CAAC;AAEF,eAAO,MAAM,WAAW,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,SAI9D,CAAC;AAEF,eAAO,MAAM,UAAU,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,SAI7D,CAAC;AAEF,eAAO,MAAM,cAAc,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,SAIjE,CAAC;AAEF,eAAO,MAAM,OAAO,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,SAI1D,CAAC;AAEF,eAAO,MAAM,qBAAqB,SAAU,UAAU,GAAG,IAAI,GAAG,SAAS,SAIxE,CAAC;AAeF,eAAO,MAAM,gBAAgB,kBACX,OAAO,GAAG,aAAa,KACtC,SAkBF,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { openSearchPanel, closeSearchPanel, search, findPrevious, findNext, replaceNext, replaceAll, } from '@codemirror/search';
|
|
2
|
+
export const openSearch = (view) => {
|
|
3
|
+
if (!view)
|
|
4
|
+
return;
|
|
5
|
+
openSearchPanel(view);
|
|
6
|
+
};
|
|
7
|
+
export const closeSearch = (view) => {
|
|
8
|
+
if (!view)
|
|
9
|
+
return;
|
|
10
|
+
closeSearchPanel(view);
|
|
11
|
+
};
|
|
12
|
+
export const searchNext = (view) => {
|
|
13
|
+
if (!view)
|
|
14
|
+
return;
|
|
15
|
+
findNext(view);
|
|
16
|
+
};
|
|
17
|
+
export const searchPrevious = (view) => {
|
|
18
|
+
if (!view)
|
|
19
|
+
return;
|
|
20
|
+
findPrevious(view);
|
|
21
|
+
};
|
|
22
|
+
export const replace = (view) => {
|
|
23
|
+
if (!view)
|
|
24
|
+
return;
|
|
25
|
+
replaceNext(view);
|
|
26
|
+
};
|
|
27
|
+
export const replaceAllOccurrences = (view) => {
|
|
28
|
+
if (!view)
|
|
29
|
+
return;
|
|
30
|
+
replaceAll(view);
|
|
31
|
+
};
|
|
32
|
+
const resolveSearch = (search) => {
|
|
33
|
+
if (!search)
|
|
34
|
+
return { enabled: false };
|
|
35
|
+
if (search === true) {
|
|
36
|
+
return { enabled: true };
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
enabled: search.enabled ?? true,
|
|
40
|
+
...search,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export const searchExtensions = (searchConfig = {}) => {
|
|
44
|
+
const options = resolveSearch(searchConfig);
|
|
45
|
+
if (!options.enabled)
|
|
46
|
+
return [];
|
|
47
|
+
const { top = true, caseSensitive = false, regexp = false, wholeWord = false, } = options ?? {};
|
|
48
|
+
return search({
|
|
49
|
+
top,
|
|
50
|
+
caseSensitive,
|
|
51
|
+
regexp,
|
|
52
|
+
wholeWord,
|
|
53
|
+
});
|
|
54
|
+
};
|
package/dist/types/editor.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ export interface EditorController {
|
|
|
11
11
|
foldAll(): boolean;
|
|
12
12
|
unfoldAll(): boolean;
|
|
13
13
|
format(formatter: (code: string) => string): boolean;
|
|
14
|
+
openSearch(): void;
|
|
15
|
+
closeSearch(): void;
|
|
16
|
+
searchNext(): void;
|
|
17
|
+
searchPrevious(): void;
|
|
18
|
+
replace(): void;
|
|
19
|
+
replaceAll(): void;
|
|
14
20
|
}
|
|
15
21
|
export interface CreateEditorOptions {
|
|
16
22
|
parent: HTMLElement;
|
|
@@ -19,6 +25,7 @@ export interface CreateEditorOptions {
|
|
|
19
25
|
readOnly?: boolean;
|
|
20
26
|
language: EditorLanguage;
|
|
21
27
|
languageOptions?: LanguageOptions;
|
|
28
|
+
search?: boolean | SearchOptions;
|
|
22
29
|
onChange?: (value: string) => void;
|
|
23
30
|
}
|
|
24
31
|
export interface EditorContainerProps {
|
|
@@ -28,6 +35,7 @@ export interface EditorContainerProps {
|
|
|
28
35
|
readOnly?: boolean;
|
|
29
36
|
language: EditorLanguage;
|
|
30
37
|
languageOptions?: LanguageOptions;
|
|
38
|
+
search?: boolean | SearchOptions;
|
|
31
39
|
onChange?: (value: string) => void;
|
|
32
40
|
}
|
|
33
41
|
interface BaseCodeEditorProps {
|
|
@@ -35,6 +43,7 @@ interface BaseCodeEditorProps {
|
|
|
35
43
|
readOnly?: boolean;
|
|
36
44
|
language: EditorLanguage;
|
|
37
45
|
languageOptions?: LanguageOptions;
|
|
46
|
+
search?: boolean | SearchOptions;
|
|
38
47
|
onReady?: (controller: EditorController) => void;
|
|
39
48
|
}
|
|
40
49
|
interface ControlledCodeEditorProps {
|
|
@@ -59,6 +68,14 @@ export interface JsonEditorConfig {
|
|
|
59
68
|
schemaLint?: boolean;
|
|
60
69
|
hover?: boolean;
|
|
61
70
|
autocomplete?: boolean;
|
|
71
|
+
onValidationChange?: (isValid: boolean) => void;
|
|
72
|
+
}
|
|
73
|
+
export interface SearchOptions {
|
|
74
|
+
enabled?: boolean;
|
|
75
|
+
top?: boolean;
|
|
76
|
+
caseSensitive?: boolean;
|
|
77
|
+
regexp?: boolean;
|
|
78
|
+
wholeWord?: boolean;
|
|
62
79
|
}
|
|
63
80
|
export {};
|
|
64
81
|
//# sourceMappingURL=editor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/types/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,MAAM,SAAS,GACf,OAAO,GACP,MAAM,GACN,WAAW,GACX,cAAc,GACd,gBAAgB,GAChB,oBAAoB,GACpB,sBAAsB,GACtB,eAAe,GACf,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,cAAc,CAAC;AAErB,MAAM,MAAM,cAAc,GAAG,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC5B,IAAI,CAAC,EAAE,gBAAgB,CAAC;CAQ3B;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,IAAI,UAAU,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IACvC,IAAI,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACrC,OAAO,IAAI,OAAO,CAAC;IACnB,SAAS,IAAI,OAAO,CAAC;IACrB,MAAM,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/types/editor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../src/ui/CodeEditor.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,
|
|
1
|
+
{"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../src/ui/CodeEditor.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA0ChD,CAAC"}
|
package/dist/ui/CodeEditor.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createEditorController } from '../core/editor';
|
|
|
4
4
|
import { resolveControlledInvariant } from '../core/invariants';
|
|
5
5
|
import { EditorContainer } from './';
|
|
6
6
|
export const CodeEditor = (props) => {
|
|
7
|
-
const { theme, readOnly, language, languageOptions, onChange, onReady } = props;
|
|
7
|
+
const { theme, readOnly, language, languageOptions, search, onChange, onReady, } = props;
|
|
8
8
|
const { mode, value: resolvedContent } = resolveControlledInvariant(props);
|
|
9
9
|
const [internalContent, setInternalContent] = useState(resolvedContent);
|
|
10
10
|
const controller = useMemo(() => createEditorController(), []);
|
|
@@ -17,5 +17,5 @@ export const CodeEditor = (props) => {
|
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
onReady?.(controller);
|
|
19
19
|
}, [controller, onReady]);
|
|
20
|
-
return (_jsx(EditorContainer, { value: mode === 'controlled' ? resolvedContent : internalContent, theme: theme, readOnly: readOnly, language: language, languageOptions: languageOptions, onChange: handleEditorChange, controller: controller }));
|
|
20
|
+
return (_jsx(EditorContainer, { value: mode === 'controlled' ? resolvedContent : internalContent, theme: theme, readOnly: readOnly, language: language, languageOptions: languageOptions, search: search, onChange: handleEditorChange, controller: controller }));
|
|
21
21
|
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { EditorContainerProps } from '../types';
|
|
2
|
-
export declare const EditorContainer: ({ value, theme, readOnly, language, languageOptions, controller, onChange, }: EditorContainerProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const EditorContainer: ({ value, theme, readOnly, language, languageOptions, search, controller, onChange, }: EditorContainerProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=EditorContainer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorContainer.d.ts","sourceRoot":"","sources":["../../src/ui/EditorContainer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEhD,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"EditorContainer.d.ts","sourceRoot":"","sources":["../../src/ui/EditorContainer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEhD,eAAO,MAAM,eAAe,yFASzB,oBAAoB,4CA2CtB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { createEditor } from '../core/editor';
|
|
4
|
-
export const EditorContainer = ({ value, theme, readOnly, language, languageOptions, controller, onChange, }) => {
|
|
4
|
+
export const EditorContainer = ({ value, theme, readOnly, language, languageOptions, search, controller, onChange, }) => {
|
|
5
5
|
const editorRef = useRef(null);
|
|
6
6
|
useEffect(() => {
|
|
7
7
|
const editor = editorRef.current;
|
|
@@ -14,6 +14,7 @@ export const EditorContainer = ({ value, theme, readOnly, language, languageOpti
|
|
|
14
14
|
readOnly,
|
|
15
15
|
language,
|
|
16
16
|
languageOptions,
|
|
17
|
+
search,
|
|
17
18
|
onChange,
|
|
18
19
|
});
|
|
19
20
|
controller.setView(view);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-codemirror-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A modular, extensible React code editor built on CodeMirror 6 with first-class JSON support.",
|
|
6
6
|
"type": "module",
|
|
@@ -43,11 +43,13 @@
|
|
|
43
43
|
},
|
|
44
44
|
"keywords": [
|
|
45
45
|
"react",
|
|
46
|
-
"code-editor",
|
|
47
46
|
"codemirror",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
47
|
+
"codemirror6",
|
|
48
|
+
"editor",
|
|
49
|
+
"code-editor",
|
|
50
|
+
"react-editor",
|
|
51
|
+
"react-codemirror",
|
|
52
|
+
"ide"
|
|
51
53
|
],
|
|
52
54
|
"license": "MIT"
|
|
53
|
-
}
|
|
55
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/diagnostics/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jsonDiagnostics.d.ts","sourceRoot":"","sources":["../../../src/core/diagnostics/jsonDiagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAS9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAsE/C,eAAO,MAAM,wBAAwB,aACxB,gBAAgB,KAC1B,SAuCF,CAAC"}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { hoverTooltip } from '@codemirror/view';
|
|
2
|
-
import { linter, lintGutter } from '@codemirror/lint';
|
|
3
|
-
import { autocompletion, } from '@codemirror/autocomplete';
|
|
4
|
-
import { syntaxTree } from '@codemirror/language';
|
|
5
|
-
import { jsonSchemaLinter, jsonSchemaHover, jsonCompletion, stateExtensions, } from 'codemirror-json-schema';
|
|
6
|
-
const getErrorMessage = (view, from) => {
|
|
7
|
-
const doc = view.state.doc;
|
|
8
|
-
const charBefore = from > 0 ? doc.sliceString(from - 1, from) : '';
|
|
9
|
-
const charAfter = doc.sliceString(from, from + 1);
|
|
10
|
-
if (charBefore === ',') {
|
|
11
|
-
return 'Trailing comma is not allowed in JSON';
|
|
12
|
-
}
|
|
13
|
-
if (charAfter === '}' || charAfter === ']') {
|
|
14
|
-
return 'Missing value before closing bracket';
|
|
15
|
-
}
|
|
16
|
-
if (charAfter === ':') {
|
|
17
|
-
return 'Missing value after ":"';
|
|
18
|
-
}
|
|
19
|
-
if (charBefore === ':') {
|
|
20
|
-
return 'Missing value after ":"';
|
|
21
|
-
}
|
|
22
|
-
if (!charAfter) {
|
|
23
|
-
return 'Unexpected end of JSON input';
|
|
24
|
-
}
|
|
25
|
-
return 'Invalid JSON syntax';
|
|
26
|
-
};
|
|
27
|
-
const jsonLinter = (view) => {
|
|
28
|
-
const diagnostics = [];
|
|
29
|
-
const tree = syntaxTree(view.state);
|
|
30
|
-
const docLength = view.state.doc.length;
|
|
31
|
-
tree.iterate({
|
|
32
|
-
enter(node) {
|
|
33
|
-
if (!node.type.isError)
|
|
34
|
-
return;
|
|
35
|
-
const from = node.from;
|
|
36
|
-
const to = Math.min(node.to, docLength);
|
|
37
|
-
diagnostics.push({
|
|
38
|
-
from,
|
|
39
|
-
to: from === to ? from + 1 : to,
|
|
40
|
-
severity: 'error',
|
|
41
|
-
message: getErrorMessage(view, from),
|
|
42
|
-
});
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
return diagnostics;
|
|
46
|
-
};
|
|
47
|
-
const safeJsonCompletion = (schema) => {
|
|
48
|
-
const source = jsonCompletion(schema);
|
|
49
|
-
return (ctx) => {
|
|
50
|
-
const result = source(ctx);
|
|
51
|
-
return Array.isArray(result) ? null : result;
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
export const jsonDiagnosticsExtension = (options = {}) => {
|
|
55
|
-
const { diagnostics = true, gutter = true, schema, schemaLint = !!schema, hover = !!schema, autocomplete = !!schema, } = options;
|
|
56
|
-
const extensions = [];
|
|
57
|
-
if (diagnostics) {
|
|
58
|
-
extensions.push(linter(jsonLinter));
|
|
59
|
-
if (gutter) {
|
|
60
|
-
extensions.push(lintGutter());
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (schema) {
|
|
64
|
-
extensions.push(stateExtensions(schema));
|
|
65
|
-
if (schemaLint) {
|
|
66
|
-
extensions.push(linter(jsonSchemaLinter(schema)));
|
|
67
|
-
}
|
|
68
|
-
if (hover) {
|
|
69
|
-
extensions.push(hoverTooltip(jsonSchemaHover(schema)));
|
|
70
|
-
}
|
|
71
|
-
if (autocomplete) {
|
|
72
|
-
extensions.push(autocompletion({
|
|
73
|
-
override: [safeJsonCompletion(schema)],
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return extensions;
|
|
78
|
-
};
|
|
File without changes
|
|
File without changes
|