mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 15:14:42 +00:00
[ty] Make auto-import completions opt-in via an experimental option
Instead of waiting to land auto-import until it is "ready for users," it'd be nicer to get incremental progress merged to `main`. By making it an experimental opt-in, we avoid making the default completion experience worse but permit developers and motivated users to try it.
This commit is contained in:
parent
8e52027a88
commit
0a0eaf5a9b
7 changed files with 63 additions and 16 deletions
|
@ -12,7 +12,17 @@ use crate::find_node::covering_node;
|
||||||
use crate::goto::DefinitionsOrTargets;
|
use crate::goto::DefinitionsOrTargets;
|
||||||
use crate::{Db, all_symbols};
|
use crate::{Db, all_symbols};
|
||||||
|
|
||||||
pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec<DetailedCompletion<'_>> {
|
#[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<DetailedCompletion<'db>> {
|
||||||
let parsed = parsed_module(db, file).load(db);
|
let parsed = parsed_module(db, file).load(db);
|
||||||
|
|
||||||
let Some(target_token) = CompletionTargetTokens::find(&parsed, offset) else {
|
let Some(target_token) = CompletionTargetTokens::find(&parsed, offset) else {
|
||||||
|
@ -39,13 +49,15 @@ pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec<DetailedComp
|
||||||
}
|
}
|
||||||
CompletionTargetAst::Scoped { node } => {
|
CompletionTargetAst::Scoped { node } => {
|
||||||
let mut completions = model.scoped_completions(node);
|
let mut completions = model.scoped_completions(node);
|
||||||
for symbol in all_symbols(db, "") {
|
if settings.auto_import {
|
||||||
completions.push(Completion {
|
for symbol in all_symbols(db, "") {
|
||||||
name: ast::name::Name::new(&symbol.symbol.name),
|
completions.push(Completion {
|
||||||
ty: None,
|
name: ast::name::Name::new(&symbol.symbol.name),
|
||||||
kind: symbol.symbol.kind.to_completion_kind(),
|
ty: None,
|
||||||
builtin: false,
|
kind: symbol.symbol.kind.to_completion_kind(),
|
||||||
});
|
builtin: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
completions
|
completions
|
||||||
}
|
}
|
||||||
|
@ -518,7 +530,7 @@ mod tests {
|
||||||
use crate::completion::{DetailedCompletion, completion};
|
use crate::completion::{DetailedCompletion, completion};
|
||||||
use crate::tests::{CursorTest, cursor_test};
|
use crate::tests::{CursorTest, cursor_test};
|
||||||
|
|
||||||
use super::token_suffix_by_kinds;
|
use super::{CompletionSettings, token_suffix_by_kinds};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn token_suffixes_match() {
|
fn token_suffixes_match() {
|
||||||
|
@ -3073,7 +3085,8 @@ from os.<CURSOR>
|
||||||
predicate: impl Fn(&DetailedCompletion) -> bool,
|
predicate: impl Fn(&DetailedCompletion) -> bool,
|
||||||
snapshot: impl Fn(&DetailedCompletion) -> String,
|
snapshot: impl Fn(&DetailedCompletion) -> String,
|
||||||
) -> 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() {
|
if completions.is_empty() {
|
||||||
return "<No completions found>".to_string();
|
return "<No completions found>".to_string();
|
||||||
}
|
}
|
||||||
|
@ -3096,7 +3109,8 @@ from os.<CURSOR>
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_completions_include(&self, expected: &str) {
|
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!(
|
assert!(
|
||||||
completions
|
completions
|
||||||
|
@ -3108,7 +3122,8 @@ from os.<CURSOR>
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_completions_do_not_include(&self, unexpected: &str) {
|
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!(
|
assert!(
|
||||||
completions
|
completions
|
||||||
|
|
|
@ -26,7 +26,7 @@ mod symbols;
|
||||||
mod workspace_symbols;
|
mod workspace_symbols;
|
||||||
|
|
||||||
pub use all_symbols::{AllSymbolInfo, all_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 doc_highlights::document_highlights;
|
||||||
pub use document_symbols::document_symbols;
|
pub use document_symbols::document_symbols;
|
||||||
pub use goto::{goto_declaration, goto_definition, goto_type_definition};
|
pub use goto::{goto_declaration, goto_definition, goto_type_definition};
|
||||||
|
|
|
@ -7,7 +7,7 @@ use lsp_types::{
|
||||||
};
|
};
|
||||||
use ruff_db::source::{line_index, source_text};
|
use ruff_db::source::{line_index, source_text};
|
||||||
use ruff_source_file::OneIndexed;
|
use ruff_source_file::OneIndexed;
|
||||||
use ty_ide::completion;
|
use ty_ide::{CompletionSettings, completion};
|
||||||
use ty_project::ProjectDatabase;
|
use ty_project::ProjectDatabase;
|
||||||
use ty_python_semantic::CompletionKind;
|
use ty_python_semantic::CompletionKind;
|
||||||
|
|
||||||
|
@ -55,7 +55,10 @@ impl BackgroundDocumentRequestHandler for CompletionRequestHandler {
|
||||||
&line_index,
|
&line_index,
|
||||||
snapshot.encoding(),
|
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() {
|
if completions.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -826,6 +826,7 @@ impl Session {
|
||||||
.map_err(DocumentQueryError::InvalidUrl);
|
.map_err(DocumentQueryError::InvalidUrl);
|
||||||
DocumentSnapshot {
|
DocumentSnapshot {
|
||||||
resolved_client_capabilities: self.resolved_client_capabilities,
|
resolved_client_capabilities: self.resolved_client_capabilities,
|
||||||
|
global_settings: self.global_settings.clone(),
|
||||||
workspace_settings: key
|
workspace_settings: key
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -1000,6 +1001,7 @@ impl Drop for MutIndexGuard<'_> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct DocumentSnapshot {
|
pub(crate) struct DocumentSnapshot {
|
||||||
resolved_client_capabilities: ResolvedClientCapabilities,
|
resolved_client_capabilities: ResolvedClientCapabilities,
|
||||||
|
global_settings: Arc<GlobalSettings>,
|
||||||
workspace_settings: Arc<WorkspaceSettings>,
|
workspace_settings: Arc<WorkspaceSettings>,
|
||||||
position_encoding: PositionEncoding,
|
position_encoding: PositionEncoding,
|
||||||
document_query_result: Result<DocumentQuery, DocumentQueryError>,
|
document_query_result: Result<DocumentQuery, DocumentQueryError>,
|
||||||
|
@ -1016,6 +1018,11 @@ impl DocumentSnapshot {
|
||||||
self.position_encoding
|
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.
|
/// Returns the client settings for the workspace that this document belongs to.
|
||||||
pub(crate) fn workspace_settings(&self) -> &WorkspaceSettings {
|
pub(crate) fn workspace_settings(&self) -> &WorkspaceSettings {
|
||||||
&self.workspace_settings
|
&self.workspace_settings
|
||||||
|
|
|
@ -122,6 +122,12 @@ impl ClientOptions {
|
||||||
self
|
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]
|
#[must_use]
|
||||||
pub fn with_unknown(mut self, unknown: HashMap<String, Value>) -> Self {
|
pub fn with_unknown(mut self, unknown: HashMap<String, Value>) -> Self {
|
||||||
self.unknown = unknown;
|
self.unknown = unknown;
|
||||||
|
@ -149,6 +155,7 @@ impl GlobalOptions {
|
||||||
.experimental
|
.experimental
|
||||||
.map(|experimental| ExperimentalSettings {
|
.map(|experimental| ExperimentalSettings {
|
||||||
rename: experimental.rename.unwrap_or(true),
|
rename: experimental.rename.unwrap_or(true),
|
||||||
|
auto_import: experimental.auto_import.unwrap_or(false),
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
@ -293,6 +300,12 @@ impl Combine for DiagnosticMode {
|
||||||
pub(crate) struct Experimental {
|
pub(crate) struct Experimental {
|
||||||
/// Whether to enable the experimental symbol rename feature.
|
/// Whether to enable the experimental symbol rename feature.
|
||||||
pub(crate) rename: Option<bool>,
|
pub(crate) rename: Option<bool>,
|
||||||
|
/// 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<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
|
|
|
@ -14,6 +14,10 @@ impl GlobalSettings {
|
||||||
pub(crate) fn is_rename_enabled(&self) -> bool {
|
pub(crate) fn is_rename_enabled(&self) -> bool {
|
||||||
self.experimental.rename
|
self.experimental.rename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_auto_import_enabled(&self) -> bool {
|
||||||
|
self.experimental.auto_import
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalSettings {
|
impl GlobalSettings {
|
||||||
|
@ -25,6 +29,7 @@ impl GlobalSettings {
|
||||||
#[derive(Clone, Default, Debug, PartialEq)]
|
#[derive(Clone, Default, Debug, PartialEq)]
|
||||||
pub(crate) struct ExperimentalSettings {
|
pub(crate) struct ExperimentalSettings {
|
||||||
pub(super) rename: bool,
|
pub(super) rename: bool,
|
||||||
|
pub(super) auto_import: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolved client settings for a specific workspace.
|
/// Resolved client settings for a specific workspace.
|
||||||
|
|
|
@ -415,7 +415,11 @@ impl Workspace {
|
||||||
|
|
||||||
let offset = position.to_text_size(&source, &index, self.position_encoding)?;
|
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
|
Ok(completions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue