Skip to content

feat: Implement textDocument/documentColor #20140

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

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
65 changes: 65 additions & 0 deletions crates/ide/src/document_color.rs
Original file line number Diff line number Diff line change
@@ -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<DocumentColor> {
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;
// };
6 changes: 6 additions & 0 deletions crates/ide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -439,6 +441,10 @@ impl Analysis {
})
}

pub fn document_color(&self, file_id: FileId) -> Cancellable<Vec<DocumentColor>> {
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,
Expand Down
12 changes: 12 additions & 0 deletions crates/rust-analyzer/src/handlers/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1713,6 +1713,18 @@ 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,
) -> anyhow::Result<Vec<lsp_types::ColorInformation>> {
let _p = tracing::info_span!("handle_document_color").entered();

let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);

Ok(snap.analysis.document_color(file_id)?.into_iter().map(to_proto::document_color).collect())
}

pub(crate) fn handle_inlay_hints(
snap: GlobalStateSnapshot,
params: InlayHintParams,
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-analyzer/src/lsp/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions crates/rust-analyzer/src/lsp/to_proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions crates/rust-analyzer/src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,7 @@ impl GlobalState {
.on::<NO_RETRY, lsp_request::GotoImplementation>(handlers::handle_goto_implementation)
.on::<NO_RETRY, lsp_request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
.on::<NO_RETRY, lsp_request::InlayHintRequest>(handlers::handle_inlay_hints)
.on::<NO_RETRY, lsp_request::DocumentColor>(handlers::handle_document_color)
.on_identity::<NO_RETRY, lsp_request::InlayHintResolveRequest, _>(handlers::handle_inlay_hints_resolve)
.on::<NO_RETRY, lsp_request::CodeLensRequest>(handlers::handle_code_lens)
.on_identity::<NO_RETRY, lsp_request::CodeLensResolve, _>(handlers::handle_code_lens_resolve)
Expand Down
Loading