gpui_component/input/lsp/
document_colors.rs

1use std::ops::Range;
2
3use anyhow::Result;
4use gpui::{App, Context, Hsla, Task, Window};
5use lsp_types::ColorInformation;
6use ropey::Rope;
7
8use crate::input::{InputState, Lsp, RopeExt};
9
10pub trait DocumentColorProvider {
11    /// Fetches document colors for the specified range.
12    ///
13    /// textDocument/documentColor
14    ///
15    /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentColor
16    fn document_colors(
17        &self,
18        _text: &Rope,
19        window: &mut Window,
20        cx: &mut App,
21    ) -> Task<Result<Vec<ColorInformation>>>;
22}
23
24impl Lsp {
25    /// Get document colors that intersect with the visible range (0-based row).
26    ///
27    /// Returns byte ranges and colors.
28    pub(crate) fn document_colors_for_range(
29        &self,
30        text: &Rope,
31        visible_range: &Range<usize>,
32    ) -> Vec<(Range<usize>, Hsla)> {
33        self.document_colors
34            .iter()
35            .filter_map(|(range, color)| {
36                if (range.start.line as usize) > visible_range.end
37                    || (range.end.line as usize) < visible_range.start
38                {
39                    return None;
40                }
41
42                let start = text.position_to_offset(&range.start);
43                let end = text.position_to_offset(&range.end);
44
45                Some((start..end, *color))
46            })
47            .collect()
48    }
49
50    pub(crate) fn update_document_colors(
51        &mut self,
52        text: &Rope,
53        window: &mut Window,
54        cx: &mut Context<InputState>,
55    ) {
56        let Some(provider) = self.document_color_provider.as_ref() else {
57            return;
58        };
59
60        let task = provider.document_colors(text, window, cx);
61        self._hover_task = cx.spawn_in(window, async move |editor, cx| {
62            let colors = task.await?;
63
64            editor.update(cx, |editor, cx| {
65                let mut document_colors: Vec<(lsp_types::Range, Hsla)> = colors
66                    .iter()
67                    .map(|info| {
68                        let color = gpui::Rgba {
69                            r: info.color.red,
70                            g: info.color.green,
71                            b: info.color.blue,
72                            a: info.color.alpha,
73                        }
74                        .into();
75
76                        (info.range, color)
77                    })
78                    .collect();
79                document_colors.sort_by_key(|(range, _)| range.start);
80
81                if document_colors == editor.lsp.document_colors {
82                    return;
83                }
84                editor.lsp.document_colors = document_colors;
85                cx.notify();
86            })?;
87
88            Ok(())
89        });
90    }
91}