[ty] Skip script discovery when uv script environments are disabled (#29697)

## Summary

With two files open in Home Assistant, skipping script discovery reduced
the language server’s mean time to first diagnostics from 465 ms to 354
ms on Linux (24%). Previously, ty indexed 10,768 files looking for PEP
723 scripts, even though uv integration was off by default and ty had no
script environments to prepare.

We now scan for scripts to prepare uv environments at startup and on
reload only when uv script environments are enabled (`TY_UV=scripts` or
`TY_UV=1`). With uv off, ty still reads an open script’s inline
settings.

The benchmark client doesn’t register file watchers. With dynamic file
watching enabled, ty still scans for script-specific import paths; five
paired runs showed no startup gain in that configuration.
This commit is contained in:
Charlie Marsh authored and GitHub committed 2026-10-25 07:11:44 -04:00
1 parent 157caa55a5
commit bfa2407e98
5 files changed
+15 -10

No files matched your search

+2 -2
View File
@@ -351,7 +351,7 @@ impl MainLoop {
let (check_sender, check_receiver) = crossbeam_channel::bounded(1);
// Initialize the uv environment for all scripts
let scripts: Vec<_> = db.project().script_files(db).iter().collect();
let scripts = db.project().scripts_to_synchronize(db);
self.synchronize_scripts(db, &scripts);
request_check(&check_sender);
@@ -512,7 +512,7 @@ impl MainLoop {
files_changed: true,
}
) {
let scripts: Vec<_> = db.project().script_files(db).iter().collect();
let scripts = db.project().scripts_to_synchronize(db);
self.synchronize_scripts(db, &scripts);
}
}
+10
View File
@@ -826,6 +826,16 @@ impl Project {
}
}
/// Returns the scripts whose uv environments need preparing at startup or after a reload.
pub fn scripts_to_synchronize(self, db: &dyn Db) -> Vec<File> {
// Discovering scripts reads every project file. Keep the index lazy when
// no environment synchronization will be requested.
if !self.metadata(db).use_uv().script_environments_enabled() {
return Vec::new();
}
self.script_files(db).iter().collect()
}
fn reload_files(self, db: &mut dyn Db) {
tracing::debug!("Reloading files for project `{}`", self.name(db));
+1 -1
View File
@@ -59,7 +59,7 @@ impl UseUv {
matches!(self, Self::On)
}
const fn script_environments_enabled(self) -> bool {
pub(super) const fn script_environments_enabled(self) -> bool {
matches!(self, Self::Scripts | Self::On)
}
}
+2 -2
View File
@@ -302,7 +302,7 @@ impl Session {
files_changed: true,
})
) {
let scripts: Vec<_> = db.project().script_files(db).iter().collect();
let scripts = db.project().scripts_to_synchronize(db);
Self::synchronize_closed_scripts(
db,
&scripts,
@@ -838,7 +838,7 @@ impl Session {
let untracked = previous
.map(|state| state.untracked_files_with_pushed_diagnostics)
.unwrap_or_default();
let scripts: Vec<_> = db.project().script_files(&db).iter().collect();
let scripts = db.project().scripts_to_synchronize(&db);
Self::synchronize_closed_scripts(
&mut db,
&scripts,
@@ -184,7 +184,6 @@ Settings: Settings {
Memory report:
=======SALSA STRUCTS=======
`FileRoot` metadata=[X.XXMB] fields=[X.XXMB] count=1
`File` metadata=[X.XXMB] fields=[X.XXMB] count=1
`ModuleResolveModeIngredient` metadata=[X.XXMB] fields=[X.XXMB] count=1
`Program` metadata=[X.XXMB] fields=[X.XXMB] count=1
`Project` metadata=[X.XXMB] fields=[X.XXMB] count=1
@@ -194,12 +193,8 @@ Memory report:
metadata=[X.XXMB] fields=[X.XXMB] count=1
`dynamic_resolution_paths -> alloc::boxed::Box<[ty_module_resolver::path::SearchPath]>`
metadata=[X.XXMB] fields=[X.XXMB] count=1
`script_tag -> core::option::Option<alloc::boxed::Box<ruff_python_ast::script::ScriptTag>>`
metadata=[X.XXMB] fields=[X.XXMB] count=1
`site_packages_editables -> alloc::boxed::Box<[ty_module_resolver::resolve::SitePackagesEditables]>`
metadata=[X.XXMB] fields=[X.XXMB] count=1
`source_text -> ruff_db::source::SourceText`
metadata=[X.XXMB] fields=[X.XXMB] count=1
=======SALSA SUMMARY=======
TOTAL MEMORY USAGE: [X.XXMB]
struct metadata = [X.XXMB]