mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:21 +00:00
[ty] Initial implementation of signature help provider (#19194)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This PR includes: * Implemented core signature help logic * Added new docstring method on Definition that returns a docstring for function and class definitions * Modified the display code for Signature that allows a signature string to be broken into text ranges that correspond to each parameter in the signature * Augmented Signature struct so it can track the Definition for a signature when available; this allows us to find the docstring associated with the signature * Added utility functions for parsing parameter documentation from three popular docstring formats (Google, NumPy and reST) * Implemented tests for all of the above "Signature help" is displayed by an editor when you are typing a function call expression. It is typically triggered when you type an open parenthesis. The language server provides information about the target function's signature (or multiple signatures), documentation, and parameters. Here is how this appears:  --------- Co-authored-by: UnboundVariable <unbound@gmail.com> Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
08bc6d2589
commit
b0b65c24ff
20 changed files with 1914 additions and 51 deletions
|
@ -8,8 +8,8 @@ use lsp_types::{
|
|||
ClientCapabilities, DiagnosticOptions, DiagnosticServerCapabilities, HoverProviderCapability,
|
||||
InlayHintOptions, InlayHintServerCapabilities, MessageType, SemanticTokensLegend,
|
||||
SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities,
|
||||
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
|
||||
TypeDefinitionProviderCapability, Url, WorkDoneProgressOptions,
|
||||
SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
|
||||
TextDocumentSyncOptions, TypeDefinitionProviderCapability, Url, WorkDoneProgressOptions,
|
||||
};
|
||||
use std::num::NonZeroUsize;
|
||||
use std::panic::PanicHookInfo;
|
||||
|
@ -186,6 +186,11 @@ impl Server {
|
|||
)),
|
||||
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
||||
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
||||
signature_help_provider: Some(SignatureHelpOptions {
|
||||
trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
|
||||
retrigger_characters: Some(vec![")".to_string()]),
|
||||
work_done_progress_options: lsp_types::WorkDoneProgressOptions::default(),
|
||||
}),
|
||||
inlay_hint_provider: Some(lsp_types::OneOf::Right(
|
||||
InlayHintServerCapabilities::Options(InlayHintOptions::default()),
|
||||
)),
|
||||
|
|
|
@ -58,6 +58,9 @@ pub(super) fn request(req: server::Request) -> Task {
|
|||
>(
|
||||
req, BackgroundSchedule::Worker
|
||||
),
|
||||
requests::SignatureHelpRequestHandler::METHOD => background_document_request_task::<
|
||||
requests::SignatureHelpRequestHandler,
|
||||
>(req, BackgroundSchedule::Worker),
|
||||
requests::CompletionRequestHandler::METHOD => background_document_request_task::<
|
||||
requests::CompletionRequestHandler,
|
||||
>(
|
||||
|
|
|
@ -6,6 +6,7 @@ mod inlay_hints;
|
|||
mod semantic_tokens;
|
||||
mod semantic_tokens_range;
|
||||
mod shutdown;
|
||||
mod signature_help;
|
||||
mod workspace_diagnostic;
|
||||
|
||||
pub(super) use completion::CompletionRequestHandler;
|
||||
|
@ -16,4 +17,5 @@ pub(super) use inlay_hints::InlayHintRequestHandler;
|
|||
pub(super) use semantic_tokens::SemanticTokensRequestHandler;
|
||||
pub(super) use semantic_tokens_range::SemanticTokensRangeRequestHandler;
|
||||
pub(super) use shutdown::ShutdownHandler;
|
||||
pub(super) use signature_help::SignatureHelpRequestHandler;
|
||||
pub(super) use workspace_diagnostic::WorkspaceDiagnosticRequestHandler;
|
||||
|
|
145
crates/ty_server/src/server/api/requests/signature_help.rs
Normal file
145
crates/ty_server/src/server/api/requests/signature_help.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::DocumentSnapshot;
|
||||
use crate::document::{PositionEncoding, PositionExt};
|
||||
use crate::server::api::traits::{
|
||||
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
|
||||
};
|
||||
use crate::session::client::Client;
|
||||
use lsp_types::request::SignatureHelpRequest;
|
||||
use lsp_types::{
|
||||
Documentation, ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams,
|
||||
SignatureInformation, Url,
|
||||
};
|
||||
use ruff_db::source::{line_index, source_text};
|
||||
use ty_ide::signature_help;
|
||||
use ty_project::ProjectDatabase;
|
||||
|
||||
pub(crate) struct SignatureHelpRequestHandler;
|
||||
|
||||
impl RequestHandler for SignatureHelpRequestHandler {
|
||||
type RequestType = SignatureHelpRequest;
|
||||
}
|
||||
|
||||
impl BackgroundDocumentRequestHandler for SignatureHelpRequestHandler {
|
||||
fn document_url(params: &SignatureHelpParams) -> Cow<Url> {
|
||||
Cow::Borrowed(¶ms.text_document_position_params.text_document.uri)
|
||||
}
|
||||
|
||||
fn run_with_snapshot(
|
||||
db: &ProjectDatabase,
|
||||
snapshot: DocumentSnapshot,
|
||||
_client: &Client,
|
||||
params: SignatureHelpParams,
|
||||
) -> crate::server::Result<Option<SignatureHelp>> {
|
||||
if snapshot.client_settings().is_language_services_disabled() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(file) = snapshot.file(db) else {
|
||||
tracing::debug!("Failed to resolve file for {:?}", params);
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let source = source_text(db, file);
|
||||
let line_index = line_index(db, file);
|
||||
let offset = params.text_document_position_params.position.to_text_size(
|
||||
&source,
|
||||
&line_index,
|
||||
snapshot.encoding(),
|
||||
);
|
||||
|
||||
// Extract signature help capabilities from the client
|
||||
let resolved_capabilities = snapshot.resolved_client_capabilities();
|
||||
|
||||
let Some(signature_help_info) = signature_help(db, file, offset) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Compute active parameter from the active signature
|
||||
let active_parameter = signature_help_info
|
||||
.active_signature
|
||||
.and_then(|s| signature_help_info.signatures.get(s))
|
||||
.and_then(|sig| sig.active_parameter)
|
||||
.and_then(|p| u32::try_from(p).ok());
|
||||
|
||||
// Convert from IDE types to LSP types
|
||||
let signatures = signature_help_info
|
||||
.signatures
|
||||
.into_iter()
|
||||
.map(|sig| {
|
||||
let parameters = sig
|
||||
.parameters
|
||||
.into_iter()
|
||||
.map(|param| {
|
||||
let label = if resolved_capabilities.signature_label_offset_support {
|
||||
// Find the parameter's offset in the signature label
|
||||
if let Some(start) = sig.label.find(¶m.label) {
|
||||
let encoding = snapshot.encoding();
|
||||
|
||||
// Convert byte offsets to character offsets based on negotiated encoding
|
||||
let start_char_offset = match encoding {
|
||||
PositionEncoding::UTF8 => start,
|
||||
PositionEncoding::UTF16 => {
|
||||
sig.label[..start].encode_utf16().count()
|
||||
}
|
||||
PositionEncoding::UTF32 => sig.label[..start].chars().count(),
|
||||
};
|
||||
|
||||
let end_char_offset = match encoding {
|
||||
PositionEncoding::UTF8 => start + param.label.len(),
|
||||
PositionEncoding::UTF16 => sig.label
|
||||
[..start + param.label.len()]
|
||||
.encode_utf16()
|
||||
.count(),
|
||||
PositionEncoding::UTF32 => {
|
||||
sig.label[..start + param.label.len()].chars().count()
|
||||
}
|
||||
};
|
||||
|
||||
let start_u32 =
|
||||
u32::try_from(start_char_offset).unwrap_or(u32::MAX);
|
||||
let end_u32 = u32::try_from(end_char_offset).unwrap_or(u32::MAX);
|
||||
ParameterLabel::LabelOffsets([start_u32, end_u32])
|
||||
} else {
|
||||
ParameterLabel::Simple(param.label)
|
||||
}
|
||||
} else {
|
||||
ParameterLabel::Simple(param.label)
|
||||
};
|
||||
|
||||
ParameterInformation {
|
||||
label,
|
||||
documentation: param.documentation.map(Documentation::String),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let active_parameter = if resolved_capabilities.signature_active_parameter_support {
|
||||
sig.active_parameter.and_then(|p| u32::try_from(p).ok())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
SignatureInformation {
|
||||
label: sig.label,
|
||||
documentation: sig.documentation.map(Documentation::String),
|
||||
parameters: Some(parameters),
|
||||
active_parameter,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let signature_help = SignatureHelp {
|
||||
signatures,
|
||||
active_signature: signature_help_info
|
||||
.active_signature
|
||||
.and_then(|s| u32::try_from(s).ok()),
|
||||
active_parameter,
|
||||
};
|
||||
|
||||
Ok(Some(signature_help))
|
||||
}
|
||||
}
|
||||
|
||||
impl RetriableRequestHandler for SignatureHelpRequestHandler {}
|
|
@ -22,6 +22,12 @@ pub(crate) struct ResolvedClientCapabilities {
|
|||
|
||||
/// Whether the client supports multiline semantic tokens
|
||||
pub(crate) semantic_tokens_multiline_support: bool,
|
||||
|
||||
/// Whether the client supports signature label offsets in signature help
|
||||
pub(crate) signature_label_offset_support: bool,
|
||||
|
||||
/// Whether the client supports per-signature active parameter in signature help
|
||||
pub(crate) signature_active_parameter_support: bool,
|
||||
}
|
||||
|
||||
impl ResolvedClientCapabilities {
|
||||
|
@ -95,6 +101,34 @@ impl ResolvedClientCapabilities {
|
|||
.and_then(|semantic_tokens| semantic_tokens.multiline_token_support)
|
||||
.unwrap_or(false);
|
||||
|
||||
let signature_label_offset_support = client_capabilities
|
||||
.text_document
|
||||
.as_ref()
|
||||
.and_then(|text_document| {
|
||||
text_document
|
||||
.signature_help
|
||||
.as_ref()?
|
||||
.signature_information
|
||||
.as_ref()?
|
||||
.parameter_information
|
||||
.as_ref()?
|
||||
.label_offset_support
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let signature_active_parameter_support = client_capabilities
|
||||
.text_document
|
||||
.as_ref()
|
||||
.and_then(|text_document| {
|
||||
text_document
|
||||
.signature_help
|
||||
.as_ref()?
|
||||
.signature_information
|
||||
.as_ref()?
|
||||
.active_parameter_support
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
code_action_deferred_edit_resolution: code_action_data_support
|
||||
&& code_action_edit_resolution,
|
||||
|
@ -106,6 +140,8 @@ impl ResolvedClientCapabilities {
|
|||
type_definition_link_support: declaration_link_support,
|
||||
hover_prefer_markdown,
|
||||
semantic_tokens_multiline_support,
|
||||
signature_label_offset_support,
|
||||
signature_active_parameter_support,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue