mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
red_knot_server: add auto-completion MVP
This PR does the wiring necessary to respond to completion requests from LSP clients. As far as the actual completion results go, they are nearly about the dumbest and simplest thing we can do: we simply return a de-duplicated list of all identifiers from the current module.
This commit is contained in:
parent
163d526407
commit
b7ce694162
7 changed files with 109 additions and 1 deletions
39
crates/red_knot_ide/src/completion.rs
Normal file
39
crates/red_knot_ide/src/completion.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use ruff_db::files::File;
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_python_ast::visitor::source_order::SourceOrderVisitor;
|
||||
use ruff_python_ast::{AnyNodeRef, Identifier};
|
||||
use ruff_text_size::TextSize;
|
||||
|
||||
use crate::Db;
|
||||
|
||||
pub struct Completion {
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
pub fn completion(db: &dyn Db, file: File, _offset: TextSize) -> Vec<Completion> {
|
||||
let parsed = parsed_module(db.upcast(), file);
|
||||
identifiers(parsed.syntax().into())
|
||||
.into_iter()
|
||||
.map(|label| Completion { label })
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn identifiers(node: AnyNodeRef) -> Vec<String> {
|
||||
struct Visitor {
|
||||
identifiers: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> SourceOrderVisitor<'a> for Visitor {
|
||||
fn visit_identifier(&mut self, id: &'a Identifier) {
|
||||
self.identifiers.push(id.id.as_str().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = Visitor {
|
||||
identifiers: vec![],
|
||||
};
|
||||
node.visit_source_order(&mut visitor);
|
||||
visitor.identifiers.sort();
|
||||
visitor.identifiers.dedup();
|
||||
visitor.identifiers
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod completion;
|
||||
mod db;
|
||||
mod find_node;
|
||||
mod goto;
|
||||
|
@ -5,6 +6,7 @@ mod hover;
|
|||
mod inlay_hints;
|
||||
mod markup;
|
||||
|
||||
pub use completion::completion;
|
||||
pub use db::Db;
|
||||
pub use goto::goto_type_definition;
|
||||
pub use hover::hover;
|
||||
|
|
|
@ -227,6 +227,9 @@ impl Server {
|
|||
inlay_hint_provider: Some(lsp_types::OneOf::Right(
|
||||
InlayHintServerCapabilities::Options(InlayHintOptions::default()),
|
||||
)),
|
||||
completion_provider: Some(lsp_types::CompletionOptions {
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,11 @@ pub(super) fn request<'a>(req: server::Request) -> Task<'a> {
|
|||
request::InlayHintRequestHandler::METHOD => background_request_task::<
|
||||
request::InlayHintRequestHandler,
|
||||
>(req, BackgroundSchedule::Worker),
|
||||
request::CompletionRequestHandler::METHOD => background_request_task::<
|
||||
request::CompletionRequestHandler,
|
||||
>(
|
||||
req, BackgroundSchedule::LatencySensitive
|
||||
),
|
||||
|
||||
method => {
|
||||
tracing::warn!("Received request {method} which does not have a handler");
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
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;
|
||||
|
|
58
crates/red_knot_server/src/server/api/requests/completion.rs
Normal file
58
crates/red_knot_server/src/server/api/requests/completion.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use lsp_types::request::Completion;
|
||||
use lsp_types::{CompletionItem, CompletionParams, CompletionResponse, Url};
|
||||
use red_knot_ide::completion;
|
||||
use red_knot_project::ProjectDatabase;
|
||||
use ruff_db::source::{line_index, source_text};
|
||||
|
||||
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(¶ms.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))
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ pub(in crate::server) enum BackgroundSchedule {
|
|||
Fmt,
|
||||
/// The task should be run on the general high-priority background
|
||||
/// thread. Reserved for actions caused by the user typing (e.g.syntax highlighting).
|
||||
#[expect(dead_code)]
|
||||
LatencySensitive,
|
||||
/// The task should be run on a regular-priority background thread.
|
||||
/// The default for any request that isn't in the critical path of the user typing.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue