@@ -21,6 +21,7 @@ import type {
21
21
StreamLanguage as StreamLanguageType ,
22
22
StreamParser as StreamParserType ,
23
23
} from '@codemirror/language' ;
24
+ import type { Diagnostic } from '@codemirror/lint' ;
24
25
import type { Extension } from '@codemirror/state' ;
25
26
import type {
26
27
EditorView as EditorViewType ,
@@ -40,6 +41,7 @@ export interface HdsCodeEditorSignature {
40
41
ariaLabel ?: string ;
41
42
ariaLabelledBy ?: string ;
42
43
hasLineWrapping ?: boolean ;
44
+ isLintingEnabled ?: boolean ;
43
45
language ?: HdsCodeEditorLanguages ;
44
46
value ?: string ;
45
47
onInput ?: ( newVal : string ) => void ;
@@ -273,7 +275,7 @@ export default class HdsCodeEditorModifier extends Modifier<HdsCodeEditorSignatu
273
275
274
276
private _buildExtensionsTask = task (
275
277
{ drop : true } ,
276
- async ( { language, hasLineWrapping } ) => {
278
+ async ( { language, hasLineWrapping, isLintingEnabled } ) => {
277
279
const [
278
280
{
279
281
keymap,
@@ -314,6 +316,35 @@ export default class HdsCodeEditorModifier extends Modifier<HdsCodeEditorSignatu
314
316
hasLineWrapping ? EditorView . lineWrapping : [ ]
315
317
) ;
316
318
319
+ let lintingExtensions : Extension [ ] = [ ] ;
320
+
321
+ if ( isLintingEnabled && language === 'json' ) {
322
+ const [ { linter, lintGutter } , { syntaxTree } ] = await Promise . all ( [
323
+ import ( '@codemirror/lint' ) ,
324
+ import ( '@codemirror/language' ) ,
325
+ ] ) ;
326
+
327
+ lintingExtensions = [
328
+ linter ( ( view ) : Diagnostic [ ] => {
329
+ const diagnostics : Diagnostic [ ] = [ ] ;
330
+ try {
331
+ JSON . parse ( view . state . doc . toString ( ) ) ;
332
+ } catch ( error : any ) {
333
+ const message = error . message ;
334
+ const pos = error . position || 0 ;
335
+ diagnostics . push ( {
336
+ from : pos ,
337
+ to : pos + 1 ,
338
+ severity : 'error' ,
339
+ message,
340
+ } ) ;
341
+ }
342
+ return diagnostics ;
343
+ } ) ,
344
+ lintGutter ( ) ,
345
+ ] ;
346
+ }
347
+
317
348
let extensions = [
318
349
lineWrappingExtension ,
319
350
bracketMatching ( ) ,
@@ -334,6 +365,10 @@ export default class HdsCodeEditorModifier extends Modifier<HdsCodeEditorSignatu
334
365
extensions = [ languageExtension , ...extensions ] ;
335
366
}
336
367
368
+ if ( lintingExtensions . length !== 0 ) {
369
+ extensions = [ ...extensions , ...lintingExtensions ] ;
370
+ }
371
+
337
372
return extensions ;
338
373
}
339
374
) ;
@@ -343,18 +378,20 @@ export default class HdsCodeEditorModifier extends Modifier<HdsCodeEditorSignatu
343
378
async (
344
379
element : HTMLElement ,
345
380
{
381
+ isLintingEnabled,
346
382
language,
347
383
value,
348
384
hasLineWrapping,
349
385
} : Pick <
350
386
HdsCodeEditorSignature [ 'Args' ] [ 'Named' ] ,
351
- 'language' | 'value' | 'hasLineWrapping'
387
+ 'language' | 'value' | 'hasLineWrapping' | 'isLintingEnabled'
352
388
>
353
389
) => {
354
390
try {
355
391
const { EditorState } = await import ( '@codemirror/state' ) ;
356
392
357
393
const extensions = await this . _buildExtensionsTask . perform ( {
394
+ isLintingEnabled,
358
395
language,
359
396
hasLineWrapping : hasLineWrapping ?? false ,
360
397
} ) ;
@@ -393,6 +430,7 @@ export default class HdsCodeEditorModifier extends Modifier<HdsCodeEditorSignatu
393
430
ariaLabel,
394
431
ariaLabelledBy,
395
432
hasLineWrapping,
433
+ isLintingEnabled,
396
434
language,
397
435
value,
398
436
} = named ;
@@ -403,6 +441,7 @@ export default class HdsCodeEditorModifier extends Modifier<HdsCodeEditorSignatu
403
441
this . element = element ;
404
442
405
443
const editor = await this . _createEditorTask . perform ( element , {
444
+ isLintingEnabled,
406
445
language,
407
446
value,
408
447
hasLineWrapping,
@@ -424,6 +463,10 @@ export default class HdsCodeEditorModifier extends Modifier<HdsCodeEditorSignatu
424
463
ariaLabelledBy,
425
464
} ) ;
426
465
466
+ this . editor . dispatch ( {
467
+ changes : { from : 0 , to : 0 , insert : ' ' } ,
468
+ } ) ;
469
+
427
470
onSetup ?.( this . editor ) ;
428
471
}
429
472
) ;
0 commit comments