@@ -306,6 +306,18 @@ export const diagnostics_databases = new WeakMap<
306
306
DiagnosticsDatabase
307
307
> ( ) ;
308
308
309
+ interface IMarkerDefinition {
310
+ options : CodeMirror . TextMarkerOptions ;
311
+ start : IEditorPosition ;
312
+ end : IEditorPosition ;
313
+ hash : string ;
314
+ }
315
+
316
+ interface IMarkedDiagnostic {
317
+ editor : CodeMirror . Editor ;
318
+ marker : CodeMirror . TextMarker ;
319
+ }
320
+
309
321
export class DiagnosticsCM extends CodeMirrorIntegration {
310
322
private last_response : lsProtocol . PublishDiagnosticsParams ;
311
323
@@ -339,7 +351,7 @@ export class DiagnosticsCM extends CodeMirrorIntegration {
339
351
}
340
352
341
353
private unique_editor_ids : DefaultMap < CodeMirror . Editor , number > ;
342
- private marked_diagnostics : Map < string , CodeMirror . TextMarker > = new Map ( ) ;
354
+ private marked_diagnostics : Map < string , IMarkedDiagnostic > = new Map ( ) ;
343
355
/**
344
356
* Allows access to the most recent diagnostics in context of the editor.
345
357
*
@@ -477,6 +489,11 @@ export class DiagnosticsCM extends CodeMirrorIntegration {
477
489
this . filterDiagnostics ( response . diagnostics )
478
490
) ;
479
491
492
+ const markerOptionsByEditor = new Map <
493
+ CodeMirror . Editor ,
494
+ IMarkerDefinition [ ]
495
+ > ( ) ;
496
+
480
497
diagnostics_by_range . forEach (
481
498
( diagnostics : lsProtocol . Diagnostic [ ] , range : lsProtocol . Range ) => {
482
499
const start = PositionConverter . lsp_to_cm (
@@ -641,23 +658,70 @@ export class DiagnosticsCM extends CodeMirrorIntegration {
641
658
. join ( '\n' ) ,
642
659
className : classNames . join ( ' ' )
643
660
} ;
661
+
662
+ let optionsList = markerOptionsByEditor . get ( cm_editor ) ;
663
+ if ( ! optionsList ) {
664
+ optionsList = [ ] ;
665
+ markerOptionsByEditor . set ( cm_editor , optionsList ) ;
666
+ }
667
+ optionsList . push ( {
668
+ options,
669
+ start : start_in_editor ,
670
+ end : end_in_editor ,
671
+ hash : diagnostic_hash
672
+ } ) ;
673
+ }
674
+ }
675
+ ) ;
676
+
677
+ for ( const [
678
+ cmEditor ,
679
+ markerDefinitions
680
+ ] of markerOptionsByEditor . entries ( ) ) {
681
+ // note: could possibly be wrapped in `requestAnimationFrame()`
682
+ // at a risk of sometimes having an inconsistent state in database.
683
+ // note: using `operation()` significantly improves performance.
684
+ // test cases:
685
+ // - one cell with 1000 `math.pi` and `import math`; comment out import,
686
+ // wait for 1000 diagnostics, then uncomment import, wait for removal:
687
+ // - before:
688
+ // - diagnostics show up in: 13.6s (blocking!)
689
+ // - diagnostics removal in: 13.2s (blocking!)
690
+ // - after:
691
+ // - diagnostics show up in: 254ms
692
+ // - diagnostics removal in: 160.4ms
693
+ // - first open of a notebook with 10 cells, each with 88 diagnostics:
694
+ // - before: 2.75s (each time)
695
+ // - after 208.75ms (order of magnitude faster!)
696
+ // - first open of a notebook with 100 cells, each with 1 diagnostic
697
+ // this scenario is expected to have no gain (measures overhead)
698
+ // - before 280.34ms, 377ms, 399ms
699
+ // - after 385.29ms, 301.97ms, 309.4ms
700
+ cmEditor . operation ( ( ) => {
701
+ const doc = cmEditor . getDoc ( ) ;
702
+ for ( const definition of markerDefinitions ) {
644
703
let marker ;
645
704
try {
646
- marker = cm_editor
647
- . getDoc ( )
648
- . markText ( start_in_editor , end_in_editor , options ) ;
705
+ marker = doc . markText (
706
+ definition . start ,
707
+ definition . end ,
708
+ definition . options
709
+ ) ;
649
710
} catch ( e ) {
650
711
this . console . warn (
651
712
'Marking inspection (diagnostic text) failed:' ,
652
- diagnostics ,
713
+ definition ,
653
714
e
654
715
) ;
655
716
return ;
656
717
}
657
- this . marked_diagnostics . set ( diagnostic_hash , marker ) ;
718
+ this . marked_diagnostics . set ( definition . hash , {
719
+ marker,
720
+ editor : cmEditor
721
+ } ) ;
658
722
}
659
- }
660
- ) ;
723
+ } ) ;
724
+ }
661
725
662
726
// remove the markers which were not included in the new message
663
727
this . removeUnusedDiagnosticMarkers ( markers_to_retain ) ;
@@ -696,14 +760,36 @@ export class DiagnosticsCM extends CodeMirrorIntegration {
696
760
}
697
761
698
762
protected removeUnusedDiagnosticMarkers ( to_retain : Set < string > ) {
699
- this . marked_diagnostics . forEach (
700
- ( marker : CodeMirror . TextMarker , diagnostic_hash : string ) => {
701
- if ( ! to_retain . has ( diagnostic_hash ) ) {
702
- this . marked_diagnostics . delete ( diagnostic_hash ) ;
703
- marker . clear ( ) ;
763
+ const toRemoveByEditor = new Map <
764
+ CodeMirror . Editor ,
765
+ { marker : CodeMirror . TextMarker ; hash : string } [ ]
766
+ > ( ) ;
767
+
768
+ for ( const [
769
+ diagnosticHash ,
770
+ markedDiagnostic
771
+ ] of this . marked_diagnostics . entries ( ) ) {
772
+ if ( ! to_retain . has ( diagnosticHash ) ) {
773
+ let diagnosticsList = toRemoveByEditor . get ( markedDiagnostic . editor ) ;
774
+ if ( ! diagnosticsList ) {
775
+ diagnosticsList = [ ] ;
776
+ toRemoveByEditor . set ( markedDiagnostic . editor , diagnosticsList ) ;
704
777
}
778
+ diagnosticsList . push ( {
779
+ marker : markedDiagnostic . marker ,
780
+ hash : diagnosticHash
781
+ } ) ;
705
782
}
706
- ) ;
783
+ }
784
+
785
+ for ( const [ cmEditor , markers ] of toRemoveByEditor . entries ( ) ) {
786
+ cmEditor . operation ( ( ) => {
787
+ for ( const markerData of markers ) {
788
+ markerData . marker . clear ( ) ;
789
+ this . marked_diagnostics . delete ( markerData . hash ) ;
790
+ }
791
+ } ) ;
792
+ }
707
793
}
708
794
709
795
remove ( ) : void {
0 commit comments