Skip to content

[ty] Add documentation for server traits #19137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions crates/ruff_server/src/server/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ use self::traits::{NotificationHandler, RequestHandler};

use super::{Result, schedule::BackgroundSchedule};

/// Defines the `document_url` method for implementers of [`traits::Notification`] and [`traits::Request`],
/// given the parameter type used by the implementer.
/// Defines the `document_url` method for implementers of [`Notification`] and [`Request`], given
/// the request or notification parameter type.
///
/// This would only work if the parameter type has a `text_document` field with a `uri` field
/// that is of type [`lsp_types::Url`].
macro_rules! define_document_url {
($params:ident: &$p:ty) => {
fn document_url($params: &$p) -> std::borrow::Cow<lsp_types::Url> {
Expand Down
61 changes: 48 additions & 13 deletions crates/ruff_server/src/server/api/traits.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
//! A stateful LSP implementation that calls into the Ruff API.
//! Traits for handling requests and notifications from the LSP client.
//!
//! This module defines the trait abstractions used by the language server to handle incoming
//! requests and notifications from clients. It provides a type-safe way to implement LSP handlers
//! with different execution models (synchronous or asynchronous) and automatic retry capabilities.
//!
//! All request and notification handlers must implement the base traits [`RequestHandler`] and
//! [`NotificationHandler`], respectively, which associate them with specific LSP request or
//! notification types. These base traits are then extended by more specific traits that define
//! the execution model of the handler.
//!
//! The [`SyncRequestHandler`] and [`SyncNotificationHandler`] traits are for handlers that
//! executes synchronously on the main loop, providing mutable access to the [`Session`] that
//! contains the current state of the server. This is useful for handlers that need to modify
//! the server state such as when the content of a file changes.
//!
//! The [`BackgroundDocumentRequestHandler`] and [`BackgroundDocumentNotificationHandler`] traits
//! are for handlers that operate on a single document and can be executed on a background thread.
//! These handlers will have access to a snapshot of the document at the time of the request or
//! notification, allowing them to perform operations without blocking the main loop.
//!
//! The [`SyncNotificationHandler`] is the most common trait that would be used because most
//! notifications are specific to a single document and require updating the server state.
//! Similarly, the [`BackgroundDocumentRequestHandler`] is the most common request handler that
//! would be used as most requests are document-specific and can be executed in the background.
//!
//! See the `./requests` and `./notifications` directories for concrete implementations of these
//! traits in action.

use crate::session::{Client, DocumentSnapshot, Session};

Expand All @@ -12,9 +39,10 @@ pub(super) trait RequestHandler {
}

/// A request handler that needs mutable access to the session.
/// This will block the main message receiver loop, meaning that no
/// incoming requests or notifications will be handled while `run` is
/// executing. Try to avoid doing any I/O or long-running computations.
///
/// This will block the main message receiver loop, meaning that no incoming requests or
/// notifications will be handled while `run` is executing. Try to avoid doing any I/O or
/// long-running computations.
pub(super) trait SyncRequestHandler: RequestHandler {
fn run(
session: &mut Session,
Expand All @@ -24,10 +52,14 @@ pub(super) trait SyncRequestHandler: RequestHandler {
}

/// A request handler that can be run on a background thread.
///
/// This handler is specific to requests that operate on a single document.
pub(super) trait BackgroundDocumentRequestHandler: RequestHandler {
/// `document_url` can be implemented automatically with
/// `define_document_url!(params: &<YourParameterType>)` in the trait
/// implementation.
/// Returns the URL of the document that this request handler operates on.
///
/// This method can be implemented automatically using the [`define_document_url`] macro.
///
/// [`define_document_url`]: super::define_document_url
fn document_url(
params: &<<Self as RequestHandler>::RequestType as Request>::Params,
) -> std::borrow::Cow<lsp_types::Url>;
Expand All @@ -47,9 +79,10 @@ pub(super) trait NotificationHandler {
}

/// A notification handler that needs mutable access to the session.
/// This will block the main message receiver loop, meaning that no
/// incoming requests or notifications will be handled while `run` is
/// executing. Try to avoid doing any I/O or long-running computations.
///
/// This will block the main message receiver loop, meaning that no incoming requests or
/// notifications will be handled while `run` is executing. Try to avoid doing any I/O or
/// long-running computations.
pub(super) trait SyncNotificationHandler: NotificationHandler {
fn run(
session: &mut Session,
Expand All @@ -60,9 +93,11 @@ pub(super) trait SyncNotificationHandler: NotificationHandler {

/// A notification handler that can be run on a background thread.
pub(super) trait BackgroundDocumentNotificationHandler: NotificationHandler {
/// `document_url` can be implemented automatically with
/// `define_document_url!(params: &<YourParameterType>)` in the trait
/// implementation.
/// Returns the URL of the document that this notification handler operates on.
///
/// This method can be implemented automatically using the [`define_document_url`] macro.
///
/// [`define_document_url`]: super::define_document_url
fn document_url(
params: &<<Self as NotificationHandler>::NotificationType as LSPNotification>::Params,
) -> std::borrow::Cow<lsp_types::Url>;
Expand Down
4 changes: 2 additions & 2 deletions crates/ty_server/src/server/api/requests/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::borrow::Cow;
use lsp_types::request::DocumentDiagnosticRequest;
use lsp_types::{
DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportResult,
FullDocumentDiagnosticReport, RelatedFullDocumentDiagnosticReport,
FullDocumentDiagnosticReport, RelatedFullDocumentDiagnosticReport, Url,
};

use crate::server::Result;
Expand All @@ -22,7 +22,7 @@ impl RequestHandler for DocumentDiagnosticRequestHandler {
}

impl BackgroundDocumentRequestHandler for DocumentDiagnosticRequestHandler {
fn document_url(params: &DocumentDiagnosticParams) -> Cow<lsp_types::Url> {
fn document_url(params: &DocumentDiagnosticParams) -> Cow<Url> {
Cow::Borrowed(&params.text_document.uri)
}

Expand Down
89 changes: 68 additions & 21 deletions crates/ty_server/src/server/api/traits.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,61 @@
//! A stateful LSP implementation that calls into the ty API.
//! Traits for handling requests and notifications from the LSP client.
//!
//! This module defines the trait abstractions used by the language server to handle incoming
//! requests and notifications from clients. It provides a type-safe way to implement LSP handlers
//! with different execution models (synchronous or asynchronous) and automatic retry capabilities.
//!
//! All request and notification handlers must implement the base traits [`RequestHandler`] and
//! [`NotificationHandler`], respectively, which associate them with specific LSP request or
//! notification types. These base traits are then extended by more specific traits that define
//! the execution model of the handler.
//!
//! The [`SyncRequestHandler`] and [`SyncNotificationHandler`] traits are for handlers that
//! executes synchronously on the main loop, providing mutable access to the [`Session`] that
//! contains the current state of the server. This is useful for handlers that need to modify
//! the server state such as when the content of a file changes.
//!
//! The [`BackgroundDocumentRequestHandler`] and [`BackgroundDocumentNotificationHandler`] traits
//! are for handlers that operate on a single document and can be executed on a background thread.
//! These handlers will have access to a snapshot of the document at the time of the request or
//! notification, allowing them to perform operations without blocking the main loop. There is also
//! the [`BackgroundRequestHandler`] trait for handlers that operate on the entire session, which
//! includes all the workspaces, instead of a single document and can also be executed on a
//! background thread like fetching the workspace diagnostics.
//!
//! The [`RetriableRequestHandler`] trait is a marker trait for handlers that can be retried if the
//! Salsa database is modified during execution.
//!
//! The [`SyncNotificationHandler`] is the most common trait that would be used because most
//! notifications are specific to a single document and require updating the server state.
//! Similarly, the [`BackgroundDocumentRequestHandler`] is the most common request handler that
//! would be used as most requests are document-specific and can be executed in the background.
//!
//! See the `./requests` and `./notifications` directories for concrete implementations of these
//! traits in action.

use std::borrow::Cow;

use std::panic::AssertUnwindSafe;

use crate::session::client::Client;
use crate::session::{DocumentSnapshot, Session, SessionSnapshot};

use lsp_types::notification::Notification as LSPNotification;
use lsp_types::Url;
use lsp_types::notification::Notification;
use lsp_types::request::Request;
use ty_project::ProjectDatabase;

/// A supertrait for any server request handler.
pub(super) trait RequestHandler {
type RequestType: Request;
const METHOD: &'static str = <<Self as RequestHandler>::RequestType as Request>::METHOD;
const METHOD: &'static str = <<Self as RequestHandler>::RequestType>::METHOD;
}

/// A request handler that needs mutable access to the session.
/// This will block the main message receiver loop, meaning that no
/// incoming requests or notifications will be handled while `run` is
/// executing. Try to avoid doing any I/O or long-running computations.
///
/// This will block the main message receiver loop, meaning that no incoming requests or
/// notifications will be handled while `run` is executing. Try to avoid doing any I/O or
/// long-running computations.
pub(super) trait SyncRequestHandler: RequestHandler {
fn run(
session: &mut Session,
Expand All @@ -31,7 +68,13 @@ pub(super) trait RetriableRequestHandler: RequestHandler {
/// Whether this request can be cancelled if the Salsa database is modified.
const RETRY_ON_CANCELLATION: bool = false;

/// The error to return if the request was cancelled due to a modification to the Salsa database.
/// The error to return if the request was cancelled due to a modification to the Salsa
/// database.
///
/// By default, this returns a [`ContentModified`] error to indicate that the content of a
/// document has changed since the request was made.
///
/// [`ContentModified`]: lsp_server::ErrorCode::ContentModified
fn salsa_cancellation_error() -> lsp_server::ResponseError {
lsp_server::ResponseError {
code: lsp_server::ErrorCode::ContentModified as i32,
Expand All @@ -45,9 +88,10 @@ pub(super) trait RetriableRequestHandler: RequestHandler {
///
/// This handler is specific to requests that operate on a single document.
pub(super) trait BackgroundDocumentRequestHandler: RetriableRequestHandler {
/// Returns the URL of the document that this request handler operates on.
fn document_url(
params: &<<Self as RequestHandler>::RequestType as Request>::Params,
) -> std::borrow::Cow<lsp_types::Url>;
) -> Cow<Url>;

fn run_with_snapshot(
db: &ProjectDatabase,
Expand All @@ -58,6 +102,11 @@ pub(super) trait BackgroundDocumentRequestHandler: RetriableRequestHandler {
}

/// A request handler that can be run on a background thread.
///
/// Unlike [`BackgroundDocumentRequestHandler`], this handler operates on the entire session,
/// which includes all the workspaces, without being tied to a specific document. It is useful for
/// operations that require access to the entire session state, such as fetching workspace
/// diagnostics.
pub(super) trait BackgroundRequestHandler: RetriableRequestHandler {
fn run(
snapshot: AssertUnwindSafe<SessionSnapshot>,
Expand All @@ -68,35 +117,33 @@ pub(super) trait BackgroundRequestHandler: RetriableRequestHandler {

/// A supertrait for any server notification handler.
pub(super) trait NotificationHandler {
type NotificationType: LSPNotification;
const METHOD: &'static str =
<<Self as NotificationHandler>::NotificationType as LSPNotification>::METHOD;
type NotificationType: Notification;
const METHOD: &'static str = <<Self as NotificationHandler>::NotificationType>::METHOD;
}

/// A notification handler that needs mutable access to the session.
/// This will block the main message receiver loop, meaning that no
/// incoming requests or notifications will be handled while `run` is
/// executing. Try to avoid doing any I/O or long-running computations.
///
/// This will block the main message receiver loop, meaning that no incoming requests or
/// notifications will be handled while `run` is executing. Try to avoid doing any I/O or
/// long-running computations.
pub(super) trait SyncNotificationHandler: NotificationHandler {
fn run(
session: &mut Session,
client: &Client,
params: <<Self as NotificationHandler>::NotificationType as LSPNotification>::Params,
params: <<Self as NotificationHandler>::NotificationType as Notification>::Params,
) -> super::Result<()>;
}

/// A notification handler that can be run on a background thread.
pub(super) trait BackgroundDocumentNotificationHandler: NotificationHandler {
/// `document_url` can be implemented automatically with
/// `define_document_url!(params: &<YourParameterType>)` in the trait
/// implementation.
/// Returns the URL of the document that this notification handler operates on.
fn document_url(
params: &<<Self as NotificationHandler>::NotificationType as LSPNotification>::Params,
) -> std::borrow::Cow<lsp_types::Url>;
params: &<<Self as NotificationHandler>::NotificationType as Notification>::Params,
) -> Cow<Url>;

fn run_with_snapshot(
snapshot: DocumentSnapshot,
client: &Client,
params: <<Self as NotificationHandler>::NotificationType as LSPNotification>::Params,
params: <<Self as NotificationHandler>::NotificationType as Notification>::Params,
) -> super::Result<()>;
}
Loading