From d23e0372880819d0d9f50c163c5b5881b294d5bd Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Tue, 1 Jul 2025 15:26:59 +0100 Subject: [PATCH 1/2] feat: implement the most basic version of color provider Here, it always returns a single color and enables the language server capability --- crates/rust-analyzer/src/handlers/request.rs | 15 +++++++++++++++ crates/rust-analyzer/src/lsp/capabilities.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index a76a65220d3b..8872a8ae7091 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1713,6 +1713,21 @@ pub(crate) fn handle_ssr( to_proto::workspace_edit(&snap, source_change).map_err(Into::into) } +pub(crate) fn handle_document_color( + _snap: GlobalStateSnapshot, + _params: lsp_types::DocumentColorParams, +) -> anyhow::Result> { + let _p = tracing::info_span!("handle_document_color").entered(); + + Ok(vec![lsp_types::ColorInformation { + range: Range { + start: Position { line: 1, character: 1 }, + end: Position { line: 1, character: 4 }, + }, + color: lsp_types::Color { red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5 }, + }]) +} + pub(crate) fn handle_inlay_hints( snap: GlobalStateSnapshot, params: InlayHintParams, diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs index 04e31f37fd2c..45f6db1ef2ac 100644 --- a/crates/rust-analyzer/src/lsp/capabilities.rs +++ b/crates/rust-analyzer/src/lsp/capabilities.rs @@ -91,7 +91,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities { })), linked_editing_range_provider: None, document_link_provider: None, - color_provider: None, + color_provider: Some(lsp_types::ColorProviderCapability::Simple(true)), execute_command_provider: None, workspace: Some(WorkspaceServerCapabilities { workspace_folders: Some(WorkspaceFoldersServerCapabilities { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 0c0438c4b8ff..6e691397e918 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -1160,6 +1160,7 @@ impl GlobalState { .on::(handlers::handle_goto_implementation) .on::(handlers::handle_goto_type_definition) .on::(handlers::handle_inlay_hints) + .on::(handlers::handle_document_color) .on_identity::(handlers::handle_inlay_hints_resolve) .on::(handlers::handle_code_lens) .on_identity::(handlers::handle_code_lens_resolve) From 11ff5af3ba17013eca8434b1547d4333f120969d Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Tue, 1 Jul 2025 21:07:41 +0100 Subject: [PATCH 2/2] refactor: use the expected structure for a document method --- crates/ide/src/document_color.rs | 65 ++++++++++++++++++++ crates/ide/src/lib.rs | 6 ++ crates/rust-analyzer/src/handlers/request.rs | 15 ++--- crates/rust-analyzer/src/lsp/to_proto.rs | 10 +++ 4 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 crates/ide/src/document_color.rs diff --git a/crates/ide/src/document_color.rs b/crates/ide/src/document_color.rs new file mode 100644 index 000000000000..b3e445ac7cec --- /dev/null +++ b/crates/ide/src/document_color.rs @@ -0,0 +1,65 @@ +#![allow(warnings)] + +use hir::{EditionedFileId, Semantics}; +use ide_db::RootDatabase; +use span::FileId; +use syntax::AstNode as _; + +pub struct DocumentColor { + pub start_line: u32, + pub start_char: u32, + pub end_char: u32, + pub end_line: u32, + pub r: f32, + pub g: f32, + pub b: f32, + pub a: f32, +} + +/// Document colors let IDEs annotate regions of source code as having a particular color. +/// +/// The advantage over `textDocument/documentHighlight` is that IDEs can create virtual text +/// to show swatches, representing the color. +/// +/// # Basic Plan +/// +/// 1. Visit every expression that is known at compile time (is `const`) +/// 1. If the type `struct` of the expression contains +/// an attribute `#[rust_analyzer::color::rgb]`, proceed +/// 1. If there are 3 `f32` fields with attribute `#[rust_analyzer::color::rgb::{r,g,b}]`, +/// then we have all the necessary information to construct the type itself +pub(crate) fn document_color(db: &RootDatabase, file_id: FileId) -> Vec { + let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + + let file = sema.parse(file_id); + let file = file.syntax(); + + for event in file.preorder() { + let syntax::WalkEvent::Leave(syntax_node) = event else { continue }; + let Some(expr) = syntax::ast::Expr::cast(syntax_node) else { continue }; + dbg!(&expr); + + // `type_of_expr` fails... but why? + let Some(ty) = sema.type_of_expr(&expr) else { continue }; + dbg!(ty); + } + + vec![DocumentColor { + start_line: 1, + start_char: 1, + end_char: 1, + end_line: 1, + r: 1.0, + g: 0.0, + b: 0.0, + a: 1.0, + }] +} + +// ^^^^^ INFO: `sema.scope(file)` is NONE +// let Some(scope) = sema.scope(file) else { +// return None; +// }; diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b3b8deb61fc0..885ca2651aec 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -22,6 +22,7 @@ mod annotations; mod call_hierarchy; mod child_modules; mod doc_links; +mod document_color; mod expand_macro; mod extend_selection; mod fetch_crates; @@ -80,6 +81,7 @@ use crate::navigation_target::ToNav; pub use crate::{ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, call_hierarchy::{CallHierarchyConfig, CallItem}, + document_color::DocumentColor, expand_macro::ExpandedMacro, file_structure::{StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, @@ -439,6 +441,10 @@ impl Analysis { }) } + pub fn document_color(&self, file_id: FileId) -> Cancellable> { + self.with_db(|db| document_color::document_color(db, file_id)) + } + /// Returns a list of the places in the file where type hints can be displayed. pub fn inlay_hints( &self, diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 8872a8ae7091..f1da2c3ef681 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1713,19 +1713,16 @@ pub(crate) fn handle_ssr( to_proto::workspace_edit(&snap, source_change).map_err(Into::into) } +#[allow(unused)] pub(crate) fn handle_document_color( - _snap: GlobalStateSnapshot, - _params: lsp_types::DocumentColorParams, + snap: GlobalStateSnapshot, + params: lsp_types::DocumentColorParams, ) -> anyhow::Result> { let _p = tracing::info_span!("handle_document_color").entered(); - Ok(vec![lsp_types::ColorInformation { - range: Range { - start: Position { line: 1, character: 1 }, - end: Position { line: 1, character: 4 }, - }, - color: lsp_types::Color { red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5 }, - }]) + let file_id = try_default!(from_proto::file_id(&snap, ¶ms.text_document.uri)?); + + Ok(snap.analysis.document_color(file_id)?.into_iter().map(to_proto::document_color).collect()) } pub(crate) fn handle_inlay_hints( diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 8a848fb848cc..c6c6a51b5f6f 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -549,6 +549,16 @@ pub(crate) fn signature_help( } } +pub(crate) fn document_color(c: ide::DocumentColor) -> lsp_types::ColorInformation { + lsp_types::ColorInformation { + range: lsp_types::Range { + start: lsp_types::Position { line: c.start_line, character: c.start_char }, + end: lsp_types::Position { line: c.end_line, character: c.end_char }, + }, + color: lsp_types::Color { red: c.r, green: c.g, blue: c.b, alpha: c.a }, + } +} + pub(crate) fn inlay_hint( snap: &GlobalStateSnapshot, fields_to_resolve: &InlayFieldsToResolve,