diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index 4d9959e9e2..80239b18e1 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -12,7 +12,17 @@ use crate::find_node::covering_node; use crate::goto::DefinitionsOrTargets; use crate::{Db, all_symbols}; -pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec> { +#[derive(Clone, Debug, Default)] +pub struct CompletionSettings { + pub auto_import: bool, +} + +pub fn completion<'db>( + db: &'db dyn Db, + settings: &CompletionSettings, + file: File, + offset: TextSize, +) -> Vec> { let parsed = parsed_module(db, file).load(db); let Some(target_token) = CompletionTargetTokens::find(&parsed, offset) else { @@ -39,13 +49,15 @@ pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec { let mut completions = model.scoped_completions(node); - for symbol in all_symbols(db, "") { - completions.push(Completion { - name: ast::name::Name::new(&symbol.symbol.name), - ty: None, - kind: symbol.symbol.kind.to_completion_kind(), - builtin: false, - }); + if settings.auto_import { + for symbol in all_symbols(db, "") { + completions.push(Completion { + name: ast::name::Name::new(&symbol.symbol.name), + ty: None, + kind: symbol.symbol.kind.to_completion_kind(), + builtin: false, + }); + } } completions } @@ -518,7 +530,7 @@ mod tests { use crate::completion::{DetailedCompletion, completion}; use crate::tests::{CursorTest, cursor_test}; - use super::token_suffix_by_kinds; + use super::{CompletionSettings, token_suffix_by_kinds}; #[test] fn token_suffixes_match() { @@ -3073,7 +3085,8 @@ from os. predicate: impl Fn(&DetailedCompletion) -> bool, snapshot: impl Fn(&DetailedCompletion) -> String, ) -> String { - let completions = completion(&self.db, self.cursor.file, self.cursor.offset); + let settings = CompletionSettings::default(); + let completions = completion(&self.db, &settings, self.cursor.file, self.cursor.offset); if completions.is_empty() { return "".to_string(); } @@ -3096,7 +3109,8 @@ from os. #[track_caller] fn assert_completions_include(&self, expected: &str) { - let completions = completion(&self.db, self.cursor.file, self.cursor.offset); + let settings = CompletionSettings::default(); + let completions = completion(&self.db, &settings, self.cursor.file, self.cursor.offset); assert!( completions @@ -3108,7 +3122,8 @@ from os. #[track_caller] fn assert_completions_do_not_include(&self, unexpected: &str) { - let completions = completion(&self.db, self.cursor.file, self.cursor.offset); + let settings = CompletionSettings::default(); + let completions = completion(&self.db, &settings, self.cursor.file, self.cursor.offset); assert!( completions diff --git a/crates/ty_ide/src/lib.rs b/crates/ty_ide/src/lib.rs index b23c54d462..c481c5ad14 100644 --- a/crates/ty_ide/src/lib.rs +++ b/crates/ty_ide/src/lib.rs @@ -26,7 +26,7 @@ mod symbols; mod workspace_symbols; pub use all_symbols::{AllSymbolInfo, all_symbols}; -pub use completion::completion; +pub use completion::{CompletionSettings, completion}; pub use doc_highlights::document_highlights; pub use document_symbols::document_symbols; pub use goto::{goto_declaration, goto_definition, goto_type_definition}; diff --git a/crates/ty_server/src/server/api/requests/completion.rs b/crates/ty_server/src/server/api/requests/completion.rs index c2c5d49e4e..4307ad6dbc 100644 --- a/crates/ty_server/src/server/api/requests/completion.rs +++ b/crates/ty_server/src/server/api/requests/completion.rs @@ -7,7 +7,7 @@ use lsp_types::{ }; use ruff_db::source::{line_index, source_text}; use ruff_source_file::OneIndexed; -use ty_ide::completion; +use ty_ide::{CompletionSettings, completion}; use ty_project::ProjectDatabase; use ty_python_semantic::CompletionKind; @@ -55,7 +55,10 @@ impl BackgroundDocumentRequestHandler for CompletionRequestHandler { &line_index, snapshot.encoding(), ); - let completions = completion(db, file, offset); + let settings = CompletionSettings { + auto_import: snapshot.global_settings().is_auto_import_enabled(), + }; + let completions = completion(db, &settings, file, offset); if completions.is_empty() { return Ok(None); } diff --git a/crates/ty_server/src/session.rs b/crates/ty_server/src/session.rs index 4c53371c24..5e8b84ecf6 100644 --- a/crates/ty_server/src/session.rs +++ b/crates/ty_server/src/session.rs @@ -826,6 +826,7 @@ impl Session { .map_err(DocumentQueryError::InvalidUrl); DocumentSnapshot { resolved_client_capabilities: self.resolved_client_capabilities, + global_settings: self.global_settings.clone(), workspace_settings: key .as_ref() .ok() @@ -1000,6 +1001,7 @@ impl Drop for MutIndexGuard<'_> { #[derive(Debug)] pub(crate) struct DocumentSnapshot { resolved_client_capabilities: ResolvedClientCapabilities, + global_settings: Arc, workspace_settings: Arc, position_encoding: PositionEncoding, document_query_result: Result, @@ -1016,6 +1018,11 @@ impl DocumentSnapshot { self.position_encoding } + /// Returns the client settings for all workspaces. + pub(crate) fn global_settings(&self) -> &GlobalSettings { + &self.global_settings + } + /// Returns the client settings for the workspace that this document belongs to. pub(crate) fn workspace_settings(&self) -> &WorkspaceSettings { &self.workspace_settings diff --git a/crates/ty_server/src/session/options.rs b/crates/ty_server/src/session/options.rs index bf5f048f9f..e890f05fef 100644 --- a/crates/ty_server/src/session/options.rs +++ b/crates/ty_server/src/session/options.rs @@ -122,6 +122,12 @@ impl ClientOptions { self } + #[must_use] + pub fn with_experimental_auto_import(mut self, enabled: bool) -> Self { + self.global.experimental.get_or_insert_default().auto_import = Some(enabled); + self + } + #[must_use] pub fn with_unknown(mut self, unknown: HashMap) -> Self { self.unknown = unknown; @@ -149,6 +155,7 @@ impl GlobalOptions { .experimental .map(|experimental| ExperimentalSettings { rename: experimental.rename.unwrap_or(true), + auto_import: experimental.auto_import.unwrap_or(false), }) .unwrap_or_default(); @@ -293,6 +300,12 @@ impl Combine for DiagnosticMode { pub(crate) struct Experimental { /// Whether to enable the experimental symbol rename feature. pub(crate) rename: Option, + /// Whether to enable the experimental "auto-import" feature. + /// + /// At time of writing (2025-08-29), this feature is still + /// under active development. It may not work right or may be + /// incomplete. + pub(crate) auto_import: Option, } #[derive(Clone, Debug, Serialize, Deserialize, Default)] diff --git a/crates/ty_server/src/session/settings.rs b/crates/ty_server/src/session/settings.rs index 9f54c7069d..7b3e95f3ed 100644 --- a/crates/ty_server/src/session/settings.rs +++ b/crates/ty_server/src/session/settings.rs @@ -14,6 +14,10 @@ impl GlobalSettings { pub(crate) fn is_rename_enabled(&self) -> bool { self.experimental.rename } + + pub(crate) fn is_auto_import_enabled(&self) -> bool { + self.experimental.auto_import + } } impl GlobalSettings { @@ -25,6 +29,7 @@ impl GlobalSettings { #[derive(Clone, Default, Debug, PartialEq)] pub(crate) struct ExperimentalSettings { pub(super) rename: bool, + pub(super) auto_import: bool, } /// Resolved client settings for a specific workspace. diff --git a/crates/ty_wasm/src/lib.rs b/crates/ty_wasm/src/lib.rs index cf88ce6392..4bf3d19249 100644 --- a/crates/ty_wasm/src/lib.rs +++ b/crates/ty_wasm/src/lib.rs @@ -415,7 +415,11 @@ impl Workspace { let offset = position.to_text_size(&source, &index, self.position_encoding)?; - let completions = ty_ide::completion(&self.db, file_id.file, offset); + // NOTE: At time of writing, 2025-08-29, auto-import isn't + // ready to be enabled by default yet. Once it is, we should + // either just enable it or provide a way to configure it. + let settings = ty_ide::CompletionSettings { auto_import: false }; + let completions = ty_ide::completion(&self.db, &settings, file_id.file, offset); Ok(completions .into_iter()