Rename Red Knot (#17820)

This commit is contained in:
Micha Reiser 2025-05-03 19:49:15 +02:00 committed by GitHub
parent e6a798b962
commit b51c4f82ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1564 changed files with 1598 additions and 1578 deletions

View file

@ -0,0 +1,18 @@
use lsp_server::ErrorCode;
use lsp_types::{notification::PublishDiagnostics, PublishDiagnosticsParams, Url};
use crate::server::client::Notifier;
use crate::server::Result;
use super::LSPResult;
pub(super) fn clear_diagnostics(uri: &Url, notifier: &Notifier) -> Result<()> {
notifier
.notify::<PublishDiagnostics>(PublishDiagnosticsParams {
uri: uri.clone(),
diagnostics: vec![],
version: None,
})
.with_failure_code(ErrorCode::InternalError)?;
Ok(())
}

View file

@ -0,0 +1,11 @@
mod did_change;
mod did_close;
mod did_close_notebook;
mod did_open;
mod did_open_notebook;
pub(super) use did_change::DidChangeTextDocumentHandler;
pub(super) use did_close::DidCloseTextDocumentHandler;
pub(super) use did_close_notebook::DidCloseNotebookHandler;
pub(super) use did_open::DidOpenTextDocumentHandler;
pub(super) use did_open_notebook::DidOpenNotebookHandler;

View file

@ -0,0 +1,55 @@
use lsp_server::ErrorCode;
use lsp_types::notification::DidChangeTextDocument;
use lsp_types::DidChangeTextDocumentParams;
use ty_project::watch::ChangeEvent;
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
use crate::server::api::LSPResult;
use crate::server::client::{Notifier, Requester};
use crate::server::Result;
use crate::session::Session;
use crate::system::{url_to_any_system_path, AnySystemPath};
pub(crate) struct DidChangeTextDocumentHandler;
impl NotificationHandler for DidChangeTextDocumentHandler {
type NotificationType = DidChangeTextDocument;
}
impl SyncNotificationHandler for DidChangeTextDocumentHandler {
fn run(
session: &mut Session,
_notifier: Notifier,
_requester: &mut Requester,
params: DidChangeTextDocumentParams,
) -> Result<()> {
let Ok(path) = url_to_any_system_path(&params.text_document.uri) else {
return Ok(());
};
let key = session.key_from_url(params.text_document.uri);
session
.update_text_document(&key, params.content_changes, params.text_document.version)
.with_failure_code(ErrorCode::InternalError)?;
match path {
AnySystemPath::System(path) => {
let db = match session.project_db_for_path_mut(path.as_std_path()) {
Some(db) => db,
None => session.default_project_db_mut(),
};
db.apply_changes(vec![ChangeEvent::file_content_changed(path)], None);
}
AnySystemPath::SystemVirtual(virtual_path) => {
let db = session.default_project_db_mut();
db.apply_changes(vec![ChangeEvent::ChangedVirtual(virtual_path)], None);
}
}
// TODO(dhruvmanila): Publish diagnostics if the client doesn't support pull diagnostics
Ok(())
}
}

View file

@ -0,0 +1,45 @@
use lsp_server::ErrorCode;
use lsp_types::notification::DidCloseTextDocument;
use lsp_types::DidCloseTextDocumentParams;
use ty_project::watch::ChangeEvent;
use crate::server::api::diagnostics::clear_diagnostics;
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
use crate::server::api::LSPResult;
use crate::server::client::{Notifier, Requester};
use crate::server::Result;
use crate::session::Session;
use crate::system::{url_to_any_system_path, AnySystemPath};
pub(crate) struct DidCloseTextDocumentHandler;
impl NotificationHandler for DidCloseTextDocumentHandler {
type NotificationType = DidCloseTextDocument;
}
impl SyncNotificationHandler for DidCloseTextDocumentHandler {
fn run(
session: &mut Session,
notifier: Notifier,
_requester: &mut Requester,
params: DidCloseTextDocumentParams,
) -> Result<()> {
let Ok(path) = url_to_any_system_path(&params.text_document.uri) else {
return Ok(());
};
let key = session.key_from_url(params.text_document.uri);
session
.close_document(&key)
.with_failure_code(ErrorCode::InternalError)?;
if let AnySystemPath::SystemVirtual(virtual_path) = path {
let db = session.default_project_db_mut();
db.apply_changes(vec![ChangeEvent::DeletedVirtual(virtual_path)], None);
}
clear_diagnostics(key.url(), &notifier)?;
Ok(())
}
}

View file

@ -0,0 +1,42 @@
use lsp_types::notification::DidCloseNotebookDocument;
use lsp_types::DidCloseNotebookDocumentParams;
use ty_project::watch::ChangeEvent;
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
use crate::server::api::LSPResult;
use crate::server::client::{Notifier, Requester};
use crate::server::Result;
use crate::session::Session;
use crate::system::{url_to_any_system_path, AnySystemPath};
pub(crate) struct DidCloseNotebookHandler;
impl NotificationHandler for DidCloseNotebookHandler {
type NotificationType = DidCloseNotebookDocument;
}
impl SyncNotificationHandler for DidCloseNotebookHandler {
fn run(
session: &mut Session,
_notifier: Notifier,
_requester: &mut Requester,
params: DidCloseNotebookDocumentParams,
) -> Result<()> {
let Ok(path) = url_to_any_system_path(&params.notebook_document.uri) else {
return Ok(());
};
let key = session.key_from_url(params.notebook_document.uri);
session
.close_document(&key)
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
if let AnySystemPath::SystemVirtual(virtual_path) = path {
let db = session.default_project_db_mut();
db.apply_changes(vec![ChangeEvent::DeletedVirtual(virtual_path)], None);
}
Ok(())
}
}

View file

@ -0,0 +1,60 @@
use lsp_types::notification::DidOpenTextDocument;
use lsp_types::{DidOpenTextDocumentParams, TextDocumentItem};
use ruff_db::Db;
use ty_project::watch::ChangeEvent;
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
use crate::server::client::{Notifier, Requester};
use crate::server::Result;
use crate::session::Session;
use crate::system::{url_to_any_system_path, AnySystemPath};
use crate::TextDocument;
pub(crate) struct DidOpenTextDocumentHandler;
impl NotificationHandler for DidOpenTextDocumentHandler {
type NotificationType = DidOpenTextDocument;
}
impl SyncNotificationHandler for DidOpenTextDocumentHandler {
fn run(
session: &mut Session,
_notifier: Notifier,
_requester: &mut Requester,
DidOpenTextDocumentParams {
text_document:
TextDocumentItem {
uri,
text,
version,
language_id,
},
}: DidOpenTextDocumentParams,
) -> Result<()> {
let Ok(path) = url_to_any_system_path(&uri) else {
return Ok(());
};
let document = TextDocument::new(text, version).with_language_id(&language_id);
session.open_text_document(uri, document);
match path {
AnySystemPath::System(path) => {
let db = match session.project_db_for_path_mut(path.as_std_path()) {
Some(db) => db,
None => session.default_project_db_mut(),
};
db.apply_changes(vec![ChangeEvent::Opened(path)], None);
}
AnySystemPath::SystemVirtual(virtual_path) => {
let db = session.default_project_db_mut();
db.files().virtual_file(db, &virtual_path);
}
}
// TODO(dhruvmanila): Publish diagnostics if the client doesn't support pull diagnostics
Ok(())
}
}

View file

@ -0,0 +1,60 @@
use lsp_server::ErrorCode;
use lsp_types::notification::DidOpenNotebookDocument;
use lsp_types::DidOpenNotebookDocumentParams;
use ruff_db::Db;
use ty_project::watch::ChangeEvent;
use crate::document::NotebookDocument;
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
use crate::server::api::LSPResult;
use crate::server::client::{Notifier, Requester};
use crate::server::Result;
use crate::session::Session;
use crate::system::{url_to_any_system_path, AnySystemPath};
pub(crate) struct DidOpenNotebookHandler;
impl NotificationHandler for DidOpenNotebookHandler {
type NotificationType = DidOpenNotebookDocument;
}
impl SyncNotificationHandler for DidOpenNotebookHandler {
fn run(
session: &mut Session,
_notifier: Notifier,
_requester: &mut Requester,
params: DidOpenNotebookDocumentParams,
) -> Result<()> {
let Ok(path) = url_to_any_system_path(&params.notebook_document.uri) else {
return Ok(());
};
let notebook = NotebookDocument::new(
params.notebook_document.version,
params.notebook_document.cells,
params.notebook_document.metadata.unwrap_or_default(),
params.cell_text_documents,
)
.with_failure_code(ErrorCode::InternalError)?;
session.open_notebook_document(params.notebook_document.uri, notebook);
match path {
AnySystemPath::System(path) => {
let db = match session.project_db_for_path_mut(path.as_std_path()) {
Some(db) => db,
None => session.default_project_db_mut(),
};
db.apply_changes(vec![ChangeEvent::Opened(path)], None);
}
AnySystemPath::SystemVirtual(virtual_path) => {
let db = session.default_project_db_mut();
db.files().virtual_file(db, &virtual_path);
}
}
// TODO(dhruvmanila): Publish diagnostics if the client doesn't support pull diagnostics
Ok(())
}
}

View file

@ -0,0 +1,11 @@
mod completion;
mod diagnostic;
mod goto_type_definition;
mod hover;
mod inlay_hints;
pub(super) use completion::CompletionRequestHandler;
pub(super) use diagnostic::DocumentDiagnosticRequestHandler;
pub(super) use goto_type_definition::GotoTypeDefinitionRequestHandler;
pub(super) use hover::HoverRequestHandler;
pub(super) use inlay_hints::InlayHintRequestHandler;

View file

@ -0,0 +1,58 @@
use std::borrow::Cow;
use lsp_types::request::Completion;
use lsp_types::{CompletionItem, CompletionParams, CompletionResponse, Url};
use ruff_db::source::{line_index, source_text};
use ty_ide::completion;
use ty_project::ProjectDatabase;
use crate::document::PositionExt;
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
use crate::server::client::Notifier;
use crate::DocumentSnapshot;
pub(crate) struct CompletionRequestHandler;
impl RequestHandler for CompletionRequestHandler {
type RequestType = Completion;
}
impl BackgroundDocumentRequestHandler for CompletionRequestHandler {
fn document_url(params: &CompletionParams) -> Cow<Url> {
Cow::Borrowed(&params.text_document_position.text_document.uri)
}
fn run_with_snapshot(
snapshot: DocumentSnapshot,
db: ProjectDatabase,
_notifier: Notifier,
params: CompletionParams,
) -> crate::server::Result<Option<CompletionResponse>> {
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.position.to_text_size(
&source,
&line_index,
snapshot.encoding(),
);
let completions = completion(&db, file, offset);
if completions.is_empty() {
return Ok(None);
}
let items: Vec<CompletionItem> = completions
.into_iter()
.map(|comp| CompletionItem {
label: comp.label,
..Default::default()
})
.collect();
let response = CompletionResponse::Array(items);
Ok(Some(response))
}
}

View file

@ -0,0 +1,106 @@
use std::borrow::Cow;
use lsp_types::request::DocumentDiagnosticRequest;
use lsp_types::{
Diagnostic, DiagnosticSeverity, DocumentDiagnosticParams, DocumentDiagnosticReport,
DocumentDiagnosticReportResult, FullDocumentDiagnosticReport, NumberOrString, Range,
RelatedFullDocumentDiagnosticReport, Url,
};
use crate::document::ToRangeExt;
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
use crate::server::{client::Notifier, Result};
use crate::session::DocumentSnapshot;
use ruff_db::diagnostic::Severity;
use ruff_db::source::{line_index, source_text};
use ty_project::{Db, ProjectDatabase};
pub(crate) struct DocumentDiagnosticRequestHandler;
impl RequestHandler for DocumentDiagnosticRequestHandler {
type RequestType = DocumentDiagnosticRequest;
}
impl BackgroundDocumentRequestHandler for DocumentDiagnosticRequestHandler {
fn document_url(params: &DocumentDiagnosticParams) -> Cow<Url> {
Cow::Borrowed(&params.text_document.uri)
}
fn run_with_snapshot(
snapshot: DocumentSnapshot,
db: ProjectDatabase,
_notifier: Notifier,
_params: DocumentDiagnosticParams,
) -> Result<DocumentDiagnosticReportResult> {
let diagnostics = compute_diagnostics(&snapshot, &db);
Ok(DocumentDiagnosticReportResult::Report(
DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport {
related_documents: None,
full_document_diagnostic_report: FullDocumentDiagnosticReport {
result_id: None,
items: diagnostics,
},
}),
))
}
}
fn compute_diagnostics(snapshot: &DocumentSnapshot, db: &ProjectDatabase) -> Vec<Diagnostic> {
let Some(file) = snapshot.file(db) else {
tracing::info!(
"No file found for snapshot for `{}`",
snapshot.query().file_url()
);
return vec![];
};
let diagnostics = match db.check_file(file) {
Ok(diagnostics) => diagnostics,
Err(cancelled) => {
tracing::info!("Diagnostics computation {cancelled}");
return vec![];
}
};
diagnostics
.as_slice()
.iter()
.map(|message| to_lsp_diagnostic(db, message, snapshot.encoding()))
.collect()
}
fn to_lsp_diagnostic(
db: &dyn Db,
diagnostic: &ruff_db::diagnostic::Diagnostic,
encoding: crate::PositionEncoding,
) -> Diagnostic {
let range = if let Some(span) = diagnostic.primary_span() {
let index = line_index(db.upcast(), span.file());
let source = source_text(db.upcast(), span.file());
span.range()
.map(|range| range.to_lsp_range(&source, &index, encoding))
.unwrap_or_default()
} else {
Range::default()
};
let severity = match diagnostic.severity() {
Severity::Info => DiagnosticSeverity::INFORMATION,
Severity::Warning => DiagnosticSeverity::WARNING,
Severity::Error | Severity::Fatal => DiagnosticSeverity::ERROR,
};
Diagnostic {
range,
severity: Some(severity),
tags: None,
code: Some(NumberOrString::String(diagnostic.id().to_string())),
code_description: None,
source: Some("ty".into()),
message: diagnostic.concise_message().to_string(),
related_information: None,
data: None,
}
}

View file

@ -0,0 +1,68 @@
use std::borrow::Cow;
use lsp_types::request::{GotoTypeDefinition, GotoTypeDefinitionParams};
use lsp_types::{GotoDefinitionResponse, Url};
use ruff_db::source::{line_index, source_text};
use ty_ide::goto_type_definition;
use ty_project::ProjectDatabase;
use crate::document::{PositionExt, ToLink};
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
use crate::server::client::Notifier;
use crate::DocumentSnapshot;
pub(crate) struct GotoTypeDefinitionRequestHandler;
impl RequestHandler for GotoTypeDefinitionRequestHandler {
type RequestType = GotoTypeDefinition;
}
impl BackgroundDocumentRequestHandler for GotoTypeDefinitionRequestHandler {
fn document_url(params: &GotoTypeDefinitionParams) -> Cow<Url> {
Cow::Borrowed(&params.text_document_position_params.text_document.uri)
}
fn run_with_snapshot(
snapshot: DocumentSnapshot,
db: ProjectDatabase,
_notifier: Notifier,
params: GotoTypeDefinitionParams,
) -> crate::server::Result<Option<GotoDefinitionResponse>> {
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(),
);
let Some(ranged) = goto_type_definition(&db, file, offset) else {
return Ok(None);
};
if snapshot
.resolved_client_capabilities()
.type_definition_link_support
{
let src = Some(ranged.range);
let links: Vec<_> = ranged
.into_iter()
.filter_map(|target| target.to_link(&db, src, snapshot.encoding()))
.collect();
Ok(Some(GotoDefinitionResponse::Link(links)))
} else {
let locations: Vec<_> = ranged
.into_iter()
.filter_map(|target| target.to_location(&db, snapshot.encoding()))
.collect();
Ok(Some(GotoDefinitionResponse::Array(locations)))
}
}
}

View file

@ -0,0 +1,71 @@
use std::borrow::Cow;
use crate::document::{PositionExt, ToRangeExt};
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
use crate::server::client::Notifier;
use crate::DocumentSnapshot;
use lsp_types::request::HoverRequest;
use lsp_types::{HoverContents, HoverParams, MarkupContent, Url};
use ruff_db::source::{line_index, source_text};
use ruff_text_size::Ranged;
use ty_ide::{hover, MarkupKind};
use ty_project::ProjectDatabase;
pub(crate) struct HoverRequestHandler;
impl RequestHandler for HoverRequestHandler {
type RequestType = HoverRequest;
}
impl BackgroundDocumentRequestHandler for HoverRequestHandler {
fn document_url(params: &HoverParams) -> Cow<Url> {
Cow::Borrowed(&params.text_document_position_params.text_document.uri)
}
fn run_with_snapshot(
snapshot: DocumentSnapshot,
db: ProjectDatabase,
_notifier: Notifier,
params: HoverParams,
) -> crate::server::Result<Option<lsp_types::Hover>> {
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(),
);
let Some(range_info) = hover(&db, file, offset) else {
return Ok(None);
};
let (markup_kind, lsp_markup_kind) = if snapshot
.resolved_client_capabilities()
.hover_prefer_markdown
{
(MarkupKind::Markdown, lsp_types::MarkupKind::Markdown)
} else {
(MarkupKind::PlainText, lsp_types::MarkupKind::PlainText)
};
let contents = range_info.display(&db, markup_kind).to_string();
Ok(Some(lsp_types::Hover {
contents: HoverContents::Markup(MarkupContent {
kind: lsp_markup_kind,
value: contents,
}),
range: Some(range_info.file_range().range().to_lsp_range(
&source,
&line_index,
snapshot.encoding(),
)),
}))
}
}

View file

@ -0,0 +1,62 @@
use std::borrow::Cow;
use crate::document::{RangeExt, TextSizeExt};
use crate::server::api::traits::{BackgroundDocumentRequestHandler, RequestHandler};
use crate::server::client::Notifier;
use crate::DocumentSnapshot;
use lsp_types::request::InlayHintRequest;
use lsp_types::{InlayHintParams, Url};
use ruff_db::source::{line_index, source_text};
use ty_ide::inlay_hints;
use ty_project::ProjectDatabase;
pub(crate) struct InlayHintRequestHandler;
impl RequestHandler for InlayHintRequestHandler {
type RequestType = InlayHintRequest;
}
impl BackgroundDocumentRequestHandler for InlayHintRequestHandler {
fn document_url(params: &InlayHintParams) -> Cow<Url> {
Cow::Borrowed(&params.text_document.uri)
}
fn run_with_snapshot(
snapshot: DocumentSnapshot,
db: ProjectDatabase,
_notifier: Notifier,
params: InlayHintParams,
) -> crate::server::Result<Option<Vec<lsp_types::InlayHint>>> {
let Some(file) = snapshot.file(&db) else {
tracing::debug!("Failed to resolve file for {:?}", params);
return Ok(None);
};
let index = line_index(&db, file);
let source = source_text(&db, file);
let range = params
.range
.to_text_range(&source, &index, snapshot.encoding());
let inlay_hints = inlay_hints(&db, file, range);
let inlay_hints = inlay_hints
.into_iter()
.map(|hint| lsp_types::InlayHint {
position: hint
.position
.to_position(&source, &index, snapshot.encoding()),
label: lsp_types::InlayHintLabel::String(hint.display(&db).to_string()),
kind: Some(lsp_types::InlayHintKind::TYPE),
tooltip: None,
padding_left: None,
padding_right: None,
data: None,
text_edits: None,
})
.collect();
Ok(Some(inlay_hints))
}
}

View file

@ -0,0 +1,78 @@
//! A stateful LSP implementation that calls into the Ruff API.
use crate::server::client::{Notifier, Requester};
use crate::session::{DocumentSnapshot, Session};
use lsp_types::notification::Notification as LSPNotification;
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;
}
/// 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.
#[expect(dead_code)]
pub(super) trait SyncRequestHandler: RequestHandler {
fn run(
session: &mut Session,
notifier: Notifier,
requester: &mut Requester,
params: <<Self as RequestHandler>::RequestType as Request>::Params,
) -> super::Result<<<Self as RequestHandler>::RequestType as Request>::Result>;
}
/// A request handler that can be run on a background thread.
pub(super) trait BackgroundDocumentRequestHandler: RequestHandler {
fn document_url(
params: &<<Self as RequestHandler>::RequestType as Request>::Params,
) -> std::borrow::Cow<lsp_types::Url>;
fn run_with_snapshot(
snapshot: DocumentSnapshot,
db: ProjectDatabase,
notifier: Notifier,
params: <<Self as RequestHandler>::RequestType as Request>::Params,
) -> super::Result<<<Self as RequestHandler>::RequestType as Request>::Result>;
}
/// 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;
}
/// 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.
pub(super) trait SyncNotificationHandler: NotificationHandler {
fn run(
session: &mut Session,
notifier: Notifier,
requester: &mut Requester,
params: <<Self as NotificationHandler>::NotificationType as LSPNotification>::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.
fn document_url(
params: &<<Self as NotificationHandler>::NotificationType as LSPNotification>::Params,
) -> std::borrow::Cow<lsp_types::Url>;
fn run_with_snapshot(
snapshot: DocumentSnapshot,
notifier: Notifier,
params: <<Self as NotificationHandler>::NotificationType as LSPNotification>::Params,
) -> super::Result<()>;
}