diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index dc7072dfd7..a52e0588b7 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -8,9 +8,11 @@ use ruff_text_size::{Ranged, TextRange, TextSize}; use ty_python_semantic::{Completion, NameKind, SemanticModel}; use crate::Db; +use crate::docstring::Docstring; use crate::find_node::covering_node; +use crate::goto::DefinitionsOrTargets; -pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec> { +pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec> { let parsed = parsed_module(db, file).load(db); let Some(target_token) = CompletionTargetTokens::find(&parsed, offset) else { @@ -40,6 +42,27 @@ pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec { + pub inner: Completion<'db>, + pub documentation: Option, +} +impl<'db> std::ops::Deref for DetailedCompletion<'db> { + type Target = Completion<'db>; + fn deref(&self) -> &Self::Target { + &self.inner + } } /// The kind of tokens identified under the cursor. @@ -478,9 +501,8 @@ fn compare_suggestions(c1: &Completion, c2: &Completion) -> Ordering { mod tests { use insta::assert_snapshot; use ruff_python_parser::{Mode, ParseOptions, TokenKind, Tokens}; - use ty_python_semantic::Completion; - use crate::completion; + use crate::completion::{DetailedCompletion, completion}; use crate::tests::{CursorTest, cursor_test}; use super::token_suffix_by_kinds; @@ -3022,14 +3044,14 @@ from os. ) } - fn completions_if(&self, predicate: impl Fn(&Completion) -> bool) -> String { + fn completions_if(&self, predicate: impl Fn(&DetailedCompletion) -> bool) -> String { self.completions_if_snapshot(predicate, |c| c.name.as_str().to_string()) } fn completions_if_snapshot( &self, - predicate: impl Fn(&Completion) -> bool, - snapshot: impl Fn(&Completion) -> String, + predicate: impl Fn(&DetailedCompletion) -> bool, + snapshot: impl Fn(&DetailedCompletion) -> String, ) -> String { let completions = completion(&self.db, self.cursor.file, self.cursor.offset); if completions.is_empty() { diff --git a/crates/ty_ide/src/goto.rs b/crates/ty_ide/src/goto.rs index 2047dce35e..52af38896b 100644 --- a/crates/ty_ide/src/goto.rs +++ b/crates/ty_ide/src/goto.rs @@ -159,6 +159,28 @@ pub(crate) enum DefinitionsOrTargets<'db> { } impl<'db> DefinitionsOrTargets<'db> { + pub(crate) fn from_ty(db: &'db dyn crate::Db, ty: Type<'db>) -> Option { + let ty_def = ty.definition(db)?; + let resolved = match ty_def { + ty_python_semantic::types::TypeDefinition::Module(module) => { + ResolvedDefinition::Module(module.file(db)?) + } + ty_python_semantic::types::TypeDefinition::Class(definition) => { + ResolvedDefinition::Definition(definition) + } + ty_python_semantic::types::TypeDefinition::Function(definition) => { + ResolvedDefinition::Definition(definition) + } + ty_python_semantic::types::TypeDefinition::TypeVar(definition) => { + ResolvedDefinition::Definition(definition) + } + ty_python_semantic::types::TypeDefinition::TypeAlias(definition) => { + ResolvedDefinition::Definition(definition) + } + }; + Some(DefinitionsOrTargets::Definitions(vec![resolved])) + } + /// Get the "goto-declaration" interpretation of this definition /// /// In this case it basically returns exactly what was found. diff --git a/crates/ty_server/src/server/api/requests/completion.rs b/crates/ty_server/src/server/api/requests/completion.rs index 946a61e3f4..ee59532f9c 100644 --- a/crates/ty_server/src/server/api/requests/completion.rs +++ b/crates/ty_server/src/server/api/requests/completion.rs @@ -2,7 +2,9 @@ use std::borrow::Cow; use std::time::Instant; use lsp_types::request::Completion; -use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, Url}; +use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, Documentation, Url, +}; use ruff_db::source::{line_index, source_text}; use ty_ide::completion; use ty_project::ProjectDatabase; @@ -64,9 +66,12 @@ impl BackgroundDocumentRequestHandler for CompletionRequestHandler { .map(|(i, comp)| { let kind = comp.kind(db).map(ty_kind_to_lsp_kind); CompletionItem { - label: comp.name.into(), + label: comp.inner.name.into(), kind, sort_text: Some(format!("{i:-max_index_len$}")), + documentation: comp + .documentation + .map(|docstring| Documentation::String(docstring.render_plaintext())), ..Default::default() } }) diff --git a/crates/ty_wasm/src/lib.rs b/crates/ty_wasm/src/lib.rs index 82fa82e4da..ced09a5164 100644 --- a/crates/ty_wasm/src/lib.rs +++ b/crates/ty_wasm/src/lib.rs @@ -421,7 +421,10 @@ impl Workspace { .into_iter() .map(|completion| Completion { kind: completion.kind(&self.db).map(CompletionKind::from), - name: completion.name.into(), + name: completion.inner.name.into(), + documentation: completion + .documentation + .map(|documentation| documentation.render_plaintext()), }) .collect()) } @@ -908,6 +911,8 @@ pub struct Completion { #[wasm_bindgen(getter_with_clone)] pub name: String, pub kind: Option, + #[wasm_bindgen(getter_with_clone)] + pub documentation: Option, } #[wasm_bindgen] diff --git a/playground/ty/src/Editor/Editor.tsx b/playground/ty/src/Editor/Editor.tsx index cc1020345f..7fd9dbfcec 100644 --- a/playground/ty/src/Editor/Editor.tsx +++ b/playground/ty/src/Editor/Editor.tsx @@ -319,6 +319,7 @@ class PlaygroundServer ? CompletionItemKind.Variable : mapCompletionKind(completion.kind), insertText: completion.name, + documentation: completion.documentation, // TODO(micha): It's unclear why this field is required for monaco but not VS Code. // and omitting it works just fine? The LSP doesn't expose this information right now // which is why we go with undefined for now.