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    pub(crate) fn update_document_colors(
50        &mut self,
51        text: &Rope,
52        window: &mut Window,
53        cx: &mut Context<InputState>,
54    ) {
55        let Some(provider) = self.document_color_provider.as_ref() else {
56            return;
57        };
58
59        let task = provider.document_colors(text, window, cx);
60        self._hover_task = cx.spawn_in(window, async move |editor, cx| {
61            let colors = task.await?;
62
63            editor.update(cx, |editor, cx| {
64                let mut document_colors: Vec<(lsp_types::Range, Hsla)> = colors
65                    .iter()
66                    .map(|info| {
67                        let color = gpui::Rgba {
68                            r: info.color.red,
69                            g: info.color.green,
70                            b: info.color.blue,
71                            a: info.color.alpha,
72                        }
73                        .into();
74
75                        (info.range, color)
76                    })
77                    .collect();
78                document_colors.sort_by_key(|(range, _)| range.start);
79
80                if document_colors == editor.lsp.document_colors {
81                    return;
82                }
83                editor.lsp.document_colors = document_colors;
84                cx.notify();
85            })?;
86
87            Ok(())
88        });
89    }
90}