Repository: helix-editor/helix
File: helix-term/src/commands/lsp.rs (Rust)

Code before:
                _ => return,
            };
            let (view, doc) = current!(editor);
            let text = doc.text();
            let pos = doc.selection(view.id).primary().cursor(text.slice(..));

            // We must find the range that contains our primary cursor to prevent our primary cursor to move
            let mut primary_index = 0;
            let ranges = document_highlights
                .iter()
                .filter_map(|highlight| lsp_range_to_range(text, highlight.range, offset_encoding))
                .enumerate()
                .map(|(i, range)| {
                    if range.contains(pos) {
                        primary_index = i;
                    }
                    range
                })
                .collect();
            let selection = Selection::new(ranges, primary_index);
            doc.set_selection(view.id, selection);
        },
    );
}

pub fn compute_lsp_annotations_for_all_views(editor: &mut Editor, jobs: &mut crate::job::Jobs) {
    let display_inlay_hints = editor.config().lsp.display_inlay_hints;
    let display_color_swatches = editor.config().lsp.display_color_swatches;

    if !display_inlay_hints && !display_color_swatches {
        return;
    }

    for (view, _) in editor.tree.views() {
        let Some(doc) = editor.documents.get(&view.doc) else {
            continue;
        };

        if display_inlay_hints {
            if let Some(callback) = compute_inlay_hints_for_view(view, doc) {
                jobs.callback(callback);
            }
        }

        if display_color_swatches {
            if let Some(callback) = compute_color_swatches_for_view(view, doc) {
                jobs.callback(callback);
            }
        }
    }
}

Review comment (suggestion):
Inlay hints and document colors are conceptually similar and can have similar implementations but currently inlay hints use the idle timeout event to make requests since inlay hints were added before the event system (#8021). I posted a work-in-progress patch to move inlay hints to the event system in #13020 that can be a guide for moving document colors into a hook as well.

Document colors are simpler because the request doesn't contain a range like inlay hints. In the inlay hint request the client can specify a range of lines and the server should only return hints within that range. This makes the behavior on the client side more nuanced because we need to re-request hints when scrolling as well as when the document changes.

With document colors we don't need to do that same tracking because the request is for the entire document. Instead we can update the colors only when a document is opened or changed. So there's no need for the `id` field on the color swatches type or to get the line ranges (`inline_annotations_line_range` should only be computed by inlay hints).

Code after:
    cx.callback(
        future,
        move |editor, _compositor, response: Option<Vec<lsp::DocumentHighlight>>| {
            let document_highlights = match response {
                Some(highlights) if !highlights.is_empty() => highlights,
                _ => return,
            };
            let (view, doc) = current!(editor);
            let text = doc.text();
            let pos = doc.selection(view.id).primary().cursor(text.slice(..));

            // We must find the range that contains our primary cursor to prevent our primary cursor to move
            let mut primary_index = 0;
            let ranges = document_highlights
                .iter()
                .filter_map(|highlight| lsp_range_to_range(text, highlight.range, offset_encoding))
                .enumerate()
                .map(|(i, range)| {
                    if range.contains(pos) {
                        primary_index = i;
                    }
                    range
                })
                .collect();
            let selection = Selection::new(ranges, primary_index);
            doc.set_selection(view.id, selection);
        },
    );
}

pub fn compute_inlay_hints_for_all_views(editor: &mut Editor, jobs: &mut crate::job::Jobs) {
    if !editor.config().lsp.display_inlay_hints {
        return;
    }

    for (view, _) in editor.tree.views() {
        let doc = match editor.documents.get(&view.doc) {
            Some(doc) => doc,
            None => continue,
        };
        if let Some(callback) = compute_inlay_hints_for_view(view, doc) {
            jobs.callback(callback);
        }
    }
}

fn compute_inlay_hints_for_view(
    view: &View,
    doc: &Document,
) -> Option<std::pin::Pin<Box<impl Future<Output = Result<crate::job::Callback, anyhow::Error>>>>> {
    let view_id = view.id;