mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-02 21:03:11 +00:00
[ty] Implemented support for "selection range" language server feature (#19567)
This PR adds support for the "selection range" language server feature. This feature was recently requested by a ty user in [this feature request](https://github.com/astral-sh/ty/issues/882). This feature allows a client to implement "smart selection expansion" based on the structure of the parse tree. For example, if you type "shift-ctrl-right-arrow" in VS Code, the current selection will be expanded to include the parent AST node. Conversely, "shift-ctrl-left-arrow" shrinks the selection. We will probably need to tune the granularity of selection expansion based on user feedback. The initial implementation includes most AST nodes, but users may find this to be too fine-grained. We have the option of skipping some AST nodes that are not as meaningful when editing code. Co-authored-by: UnboundVariable <unbound@gmail.com>
This commit is contained in:
parent
e867830848
commit
738246627f
8 changed files with 391 additions and 3 deletions
|
|
@ -7,9 +7,10 @@ use lsp_server::Connection;
|
|||
use lsp_types::{
|
||||
ClientCapabilities, DeclarationCapability, DiagnosticOptions, DiagnosticServerCapabilities,
|
||||
HoverProviderCapability, InitializeParams, InlayHintOptions, InlayHintServerCapabilities,
|
||||
MessageType, SemanticTokensLegend, SemanticTokensOptions, SemanticTokensServerCapabilities,
|
||||
ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
|
||||
TextDocumentSyncOptions, TypeDefinitionProviderCapability, Url, WorkDoneProgressOptions,
|
||||
MessageType, SelectionRangeProviderCapability, SemanticTokensLegend, SemanticTokensOptions,
|
||||
SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelpOptions,
|
||||
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
|
||||
TypeDefinitionProviderCapability, Url, WorkDoneProgressOptions,
|
||||
};
|
||||
use ruff_db::system::System;
|
||||
use std::num::NonZeroUsize;
|
||||
|
|
@ -241,6 +242,7 @@ impl Server {
|
|||
trigger_characters: Some(vec!['.'.to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
|
||||
document_symbol_provider: Some(lsp_types::OneOf::Left(true)),
|
||||
workspace_symbol_provider: Some(lsp_types::OneOf::Left(true)),
|
||||
..Default::default()
|
||||
|
|
|
|||
|
|
@ -84,6 +84,9 @@ pub(super) fn request(req: server::Request) -> Task {
|
|||
>(
|
||||
req, BackgroundSchedule::LatencySensitive
|
||||
),
|
||||
requests::SelectionRangeRequestHandler::METHOD => background_document_request_task::<
|
||||
requests::SelectionRangeRequestHandler,
|
||||
>(req, BackgroundSchedule::Worker),
|
||||
requests::DocumentSymbolRequestHandler::METHOD => background_document_request_task::<
|
||||
requests::DocumentSymbolRequestHandler,
|
||||
>(req, BackgroundSchedule::Worker),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ mod goto_references;
|
|||
mod goto_type_definition;
|
||||
mod hover;
|
||||
mod inlay_hints;
|
||||
mod selection_range;
|
||||
mod semantic_tokens;
|
||||
mod semantic_tokens_range;
|
||||
mod shutdown;
|
||||
|
|
@ -25,6 +26,7 @@ pub(super) use goto_references::ReferencesRequestHandler;
|
|||
pub(super) use goto_type_definition::GotoTypeDefinitionRequestHandler;
|
||||
pub(super) use hover::HoverRequestHandler;
|
||||
pub(super) use inlay_hints::InlayHintRequestHandler;
|
||||
pub(super) use selection_range::SelectionRangeRequestHandler;
|
||||
pub(super) use semantic_tokens::SemanticTokensRequestHandler;
|
||||
pub(super) use semantic_tokens_range::SemanticTokensRangeRequestHandler;
|
||||
pub(super) use shutdown::ShutdownHandler;
|
||||
|
|
|
|||
73
crates/ty_server/src/server/api/requests/selection_range.rs
Normal file
73
crates/ty_server/src/server/api/requests/selection_range.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use lsp_types::request::SelectionRangeRequest;
|
||||
use lsp_types::{SelectionRange as LspSelectionRange, SelectionRangeParams, Url};
|
||||
use ruff_db::source::{line_index, source_text};
|
||||
use ty_ide::selection_range;
|
||||
use ty_project::ProjectDatabase;
|
||||
|
||||
use crate::document::{PositionExt, ToRangeExt};
|
||||
use crate::server::api::traits::{
|
||||
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
|
||||
};
|
||||
use crate::session::DocumentSnapshot;
|
||||
use crate::session::client::Client;
|
||||
|
||||
pub(crate) struct SelectionRangeRequestHandler;
|
||||
|
||||
impl RequestHandler for SelectionRangeRequestHandler {
|
||||
type RequestType = SelectionRangeRequest;
|
||||
}
|
||||
|
||||
impl BackgroundDocumentRequestHandler for SelectionRangeRequestHandler {
|
||||
fn document_url(params: &SelectionRangeParams) -> Cow<Url> {
|
||||
Cow::Borrowed(¶ms.text_document.uri)
|
||||
}
|
||||
|
||||
fn run_with_snapshot(
|
||||
db: &ProjectDatabase,
|
||||
snapshot: DocumentSnapshot,
|
||||
_client: &Client,
|
||||
params: SelectionRangeParams,
|
||||
) -> crate::server::Result<Option<Vec<LspSelectionRange>>> {
|
||||
if snapshot.client_settings().is_language_services_disabled() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(file) = snapshot.file(db) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let source = source_text(db, file);
|
||||
let line_index = line_index(db, file);
|
||||
|
||||
let mut results = Vec::new();
|
||||
|
||||
for position in params.positions {
|
||||
let offset = position.to_text_size(&source, &line_index, snapshot.encoding());
|
||||
|
||||
let ranges = selection_range(db, file, offset);
|
||||
if !ranges.is_empty() {
|
||||
// Convert ranges to nested LSP SelectionRange structure
|
||||
let mut lsp_range = None;
|
||||
for &range in &ranges {
|
||||
lsp_range = Some(LspSelectionRange {
|
||||
range: range.to_lsp_range(&source, &line_index, snapshot.encoding()),
|
||||
parent: lsp_range.map(Box::new),
|
||||
});
|
||||
}
|
||||
if let Some(range) = lsp_range {
|
||||
results.push(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if results.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(results))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RetriableRequestHandler for SelectionRangeRequestHandler {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue