@@ -8,11 +8,13 @@ use rustc_hash::FxHashMap;
88use ruff_db:: diagnostic:: { Annotation , Severity , SubDiagnostic } ;
99use ruff_db:: files:: FileRange ;
1010use ruff_db:: source:: { line_index, source_text} ;
11+ use ruff_db:: system:: SystemPathBuf ;
1112use ty_project:: { Db , ProjectDatabase } ;
1213
1314use crate :: document:: { DocumentKey , FileRangeExt , ToRangeExt } ;
1415use crate :: session:: DocumentSnapshot ;
1516use crate :: session:: client:: Client ;
17+ use crate :: system:: { AnySystemPath , file_to_url} ;
1618use crate :: { PositionEncoding , Session } ;
1719
1820/// Represents the diagnostics for a text document or a notebook document.
@@ -109,6 +111,82 @@ pub(super) fn publish_diagnostics(session: &Session, key: &DocumentKey, client:
109111 }
110112}
111113
114+ /// Publishes settings diagnostics for all the project at the given path
115+ /// using the [publish diagnostics notification].
116+ ///
117+ /// [publish diagnostics notification]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics
118+ pub ( crate ) fn publish_settings_diagnostics (
119+ session : & mut Session ,
120+ client : & Client ,
121+ path : SystemPathBuf ,
122+ ) {
123+ // Don't publish settings diagnostics for workspace that are already doing full diagnostics.
124+ //
125+ // Note we DO NOT respect the fact that clients support pulls because these are
126+ // files they *specifically* won't pull diagnostics from us for, because we don't
127+ // claim to be an LSP for them.
128+ let has_workspace_diagnostics = session
129+ . workspaces ( )
130+ . for_path ( & path)
131+ . map ( |workspace| workspace. settings ( ) . diagnostic_mode ( ) . is_workspace ( ) )
132+ . unwrap_or ( false ) ;
133+ if has_workspace_diagnostics {
134+ return ;
135+ }
136+
137+ let session_encoding = session. position_encoding ( ) ;
138+ let state = session. project_state_mut ( & AnySystemPath :: System ( path) ) ;
139+ let db = & state. db ;
140+ let project = db. project ( ) ;
141+ let settings_diagnostics = project. check_settings ( db) ;
142+
143+ // We need to send diagnostics if we have non-empty ones, or we have ones to clear.
144+ // These will both almost always be empty so this function will almost always be a no-op.
145+ if settings_diagnostics. is_empty ( ) && state. untracked_files_with_pushed_diagnostics . is_empty ( ) {
146+ return ;
147+ }
148+
149+ // Group diagnostics by URL
150+ let mut diagnostics_by_url: FxHashMap < Url , Vec < _ > > = FxHashMap :: default ( ) ;
151+ for diagnostic in settings_diagnostics {
152+ if let Some ( span) = diagnostic. primary_span ( ) {
153+ let file = span. expect_ty_file ( ) ;
154+ let Some ( url) = file_to_url ( db, file) else {
155+ tracing:: debug!( "Failed to convert file to URL at {}" , file. path( db) ) ;
156+ continue ;
157+ } ;
158+ diagnostics_by_url. entry ( url) . or_default ( ) . push ( diagnostic) ;
159+ }
160+ }
161+
162+ // Record the URLs we're sending non-empty diagnostics for, so we know to clear them
163+ // the next time we publish settings diagnostics!
164+ let old_untracked = std:: mem:: replace (
165+ & mut state. untracked_files_with_pushed_diagnostics ,
166+ diagnostics_by_url. keys ( ) . cloned ( ) . collect ( ) ,
167+ ) ;
168+
169+ // Add empty diagnostics for any files that had diagnostics before but don't now.
170+ // This will clear them (either the file is no longer relevant to us or fixed!)
171+ for url in old_untracked {
172+ diagnostics_by_url. entry ( url) . or_default ( ) ;
173+ }
174+ // Send the settings diagnostics!
175+ for ( url, file_diagnostics) in diagnostics_by_url {
176+ // Convert diagnostics to LSP format
177+ let lsp_diagnostics = file_diagnostics
178+ . into_iter ( )
179+ . map ( |diagnostic| to_lsp_diagnostic ( db, & diagnostic, session_encoding) )
180+ . collect :: < Vec < _ > > ( ) ;
181+
182+ client. send_notification :: < PublishDiagnostics > ( PublishDiagnosticsParams {
183+ uri : url,
184+ diagnostics : lsp_diagnostics,
185+ version : None ,
186+ } ) ;
187+ }
188+ }
189+
112190pub ( super ) fn compute_diagnostics (
113191 db : & ProjectDatabase ,
114192 snapshot : & DocumentSnapshot ,
0 commit comments