@@ -8,6 +8,7 @@ use ide_db::FxHashMap;
88use itertools:: Itertools ;
99use nohash_hasher:: { IntMap , IntSet } ;
1010use rustc_hash:: FxHashSet ;
11+ use stdx:: iter_eq_by;
1112use triomphe:: Arc ;
1213
1314use crate :: { global_state:: GlobalStateSnapshot , lsp, lsp_ext} ;
@@ -22,14 +23,21 @@ pub struct DiagnosticsMapConfig {
2223 pub check_ignore : FxHashSet < String > ,
2324}
2425
26+ pub ( crate ) type DiagnosticsGeneration = usize ;
27+
2528#[ derive( Debug , Default , Clone ) ]
2629pub ( crate ) struct DiagnosticCollection {
2730 // FIXME: should be IntMap<FileId, Vec<ra_id::Diagnostic>>
28- pub ( crate ) native : IntMap < FileId , Vec < lsp_types:: Diagnostic > > ,
31+ pub ( crate ) native : IntMap < FileId , ( DiagnosticsGeneration , Vec < lsp_types:: Diagnostic > ) > ,
2932 // FIXME: should be Vec<flycheck::Diagnostic>
3033 pub ( crate ) check : IntMap < usize , IntMap < FileId , Vec < lsp_types:: Diagnostic > > > ,
3134 pub ( crate ) check_fixes : CheckFixes ,
3235 changes : IntSet < FileId > ,
36+ /// Counter for supplying a new generation number for diagnostics.
37+ /// This is used to keep track of when to clear the diagnostics for a given file as we compute
38+ /// diagnostics on multiple worker threads simultaneously which may result in multiple diagnostics
39+ /// updates for the same file in a single generation update (due to macros affecting multiple files).
40+ generation : DiagnosticsGeneration ,
3341}
3442
3543#[ derive( Debug , Clone ) ]
@@ -82,29 +90,39 @@ impl DiagnosticCollection {
8290
8391 pub ( crate ) fn set_native_diagnostics (
8492 & mut self ,
93+ generation : DiagnosticsGeneration ,
8594 file_id : FileId ,
86- diagnostics : Vec < lsp_types:: Diagnostic > ,
95+ mut diagnostics : Vec < lsp_types:: Diagnostic > ,
8796 ) {
88- if let Some ( existing_diagnostics) = self . native . get ( & file_id) {
97+ diagnostics. sort_by_key ( |it| ( it. range . start , it. range . end ) ) ;
98+ if let Some ( ( old_gen, existing_diagnostics) ) = self . native . get_mut ( & file_id) {
8999 if existing_diagnostics. len ( ) == diagnostics. len ( )
90- && diagnostics
91- . iter ( )
92- . zip ( existing_diagnostics)
93- . all ( |( new, existing) | are_diagnostics_equal ( new, existing) )
100+ && iter_eq_by ( & diagnostics, & * existing_diagnostics, |new, existing| {
101+ are_diagnostics_equal ( new, existing)
102+ } )
94103 {
104+ // don't signal an update if the diagnostics are the same
95105 return ;
96106 }
107+ if * old_gen < generation || generation == 0 {
108+ self . native . insert ( file_id, ( generation, diagnostics) ) ;
109+ } else {
110+ existing_diagnostics. extend ( diagnostics) ;
111+ // FIXME: Doing the merge step of a merge sort here would be a bit more performant
112+ // but eh
113+ existing_diagnostics. sort_by_key ( |it| ( it. range . start , it. range . end ) )
114+ }
115+ } else {
116+ self . native . insert ( file_id, ( generation, diagnostics) ) ;
97117 }
98-
99- self . native . insert ( file_id, diagnostics) ;
100118 self . changes . insert ( file_id) ;
101119 }
102120
103121 pub ( crate ) fn diagnostics_for (
104122 & self ,
105123 file_id : FileId ,
106124 ) -> impl Iterator < Item = & lsp_types:: Diagnostic > {
107- let native = self . native . get ( & file_id) . into_iter ( ) . flatten ( ) ;
125+ let native = self . native . get ( & file_id) . into_iter ( ) . map ( | ( _ , d ) | d ) . flatten ( ) ;
108126 let check = self . check . values ( ) . filter_map ( move |it| it. get ( & file_id) ) . flatten ( ) ;
109127 native. chain ( check)
110128 }
@@ -115,6 +133,11 @@ impl DiagnosticCollection {
115133 }
116134 Some ( mem:: take ( & mut self . changes ) )
117135 }
136+
137+ pub ( crate ) fn next_generation ( & mut self ) -> usize {
138+ self . generation += 1 ;
139+ self . generation
140+ }
118141}
119142
120143fn are_diagnostics_equal ( left : & lsp_types:: Diagnostic , right : & lsp_types:: Diagnostic ) -> bool {
@@ -126,7 +149,8 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno
126149
127150pub ( crate ) fn fetch_native_diagnostics (
128151 snapshot : GlobalStateSnapshot ,
129- subscriptions : Vec < FileId > ,
152+ subscriptions : std:: sync:: Arc < [ FileId ] > ,
153+ slice : std:: ops:: Range < usize > ,
130154) -> Vec < ( FileId , Vec < lsp_types:: Diagnostic > ) > {
131155 let _p = tracing:: info_span!( "fetch_native_diagnostics" ) . entered ( ) ;
132156 let _ctx = stdx:: panic_context:: enter ( "fetch_native_diagnostics" . to_owned ( ) ) ;
@@ -149,7 +173,7 @@ pub(crate) fn fetch_native_diagnostics(
149173 // the diagnostics produced may point to different files not requested by the concrete request,
150174 // put those into here and filter later
151175 let mut odd_ones = Vec :: new ( ) ;
152- let mut diagnostics = subscriptions
176+ let mut diagnostics = subscriptions[ slice ]
153177 . iter ( )
154178 . copied ( )
155179 . filter_map ( |file_id| {
0 commit comments