Repository: helix-editor/helix
File: helix-term/src/handlers/document_colors.rs (Rust)

Code before:

fn request_document_colors_for_view(editor: &mut Editor, view_id: ViewId, doc_id: DocumentId) {
    if !editor.config().lsp.display_color_swatches {
        return;
    }

    // Cancel any ongoing events for this view.
    if let Some(mut controller) = editor
        .handlers
        .document_colors
        .active_requests
        .remove(&view_id)
    {
        controller.cancel();
    }

    let Some(doc) = editor.document(doc_id) else {
        return;
    };

    let Some(language_server) = doc
        .language_servers_with_feature(LanguageServerFeature::DocumentColors)
        .next()
    else {
        return;
    };

    let offset_encoding = language_server.offset_encoding();

    let future = language_server
        .text_document_colors(doc.identifier(), None)
        .expect("language server must return Some if it supports document colors");

    let mut controller = TaskController::new();
    let cancel = controller.restart();
    editor
        .handlers
        .document_colors
        .active_requests
        .insert(view_id, controller);

    tokio::spawn(async move {
        match cancelable_future(future, cancel).await {
            Some(Ok(response)) => {
                job::dispatch(move |editor, _| {
                    attach_document_colors(editor, view_id, doc_id, response, offset_encoding)
                })
                .await;
            }
            Some(Err(err)) => log::error!("document color request failed: {err}"),
            None => (),

Review comment (suggestion):
This will only request colors from the first language server that can support the request. Instead we should use `doc.language_servers_with_feature` to create a `FuturesOrdered` - see uses of that type in the LSP commands module

Code after:
                    .filter_map(|color_info| {
                        let pos = helix_lsp::util::lsp_pos_to_pos(
                            &text,
                            color_info.range.start,
                            offset_encoding,
                        )?;
                        Some((pos, color_info.color))
                    })
                    .collect();
                anyhow::Ok(colors)
            }
        })
        .collect();

    tokio::spawn(async move {
        let mut all_colors = Vec::new();
        loop {
            match cancelable_future(futures.next(), &cancel).await {
                Some(Some(Ok(items))) => all_colors.extend(items),
                Some(Some(Err(err))) => log::error!("document color request failed: {err}"),
                Some(None) => break,
                // The request was cancelled.
                None => return,
            }
        }
        job::dispatch(move |editor, _| attach_document_colors(editor, doc_id, all_colors)).await;
    });
}

fn attach_document_colors(
    editor: &mut Editor,
    doc_id: DocumentId,
    mut doc_colors: Vec<(usize, lsp::Color)>,
) {
    if !editor.config().lsp.display_color_swatches {
        return;
    }

    let Some(doc) = editor.documents.get_mut(&doc_id) else {
        return;
    };

    if doc_colors.is_empty() {
        doc.color_swatches.take();
        return;
    }

    doc_colors.sort_by_key(|(pos, _)| *pos);

    let mut color_swatches = Vec::with_capacity(doc_colors.len());
    let mut color_swatches_padding = Vec::with_capacity(doc_colors.len());