From ba6e6dc3dd96e3688bb7bb5d553adb5fcb005e34 Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:16:15 +0000 Subject: [PATCH] Colors for items in the completion menu (#12299) --- helix-term/src/ui/completion.rs | 105 +++++++++++++++++++------------- helix-view/src/graphics.rs | 25 ++++++++ 2 files changed, 87 insertions(+), 43 deletions(-) diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index ad317e39f983..adacfad330f4 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -9,10 +9,13 @@ use helix_view::{ document::SavePoint, editor::CompleteAction, handlers::lsp::SignatureHelpInvoked, - theme::{Modifier, Style}, + theme::{Color, Modifier, Style}, ViewId, }; -use tui::{buffer::Buffer as Surface, text::Span}; +use tui::{ + buffer::Buffer as Surface, + text::{Span, Spans}, +}; use std::{borrow::Cow, sync::Arc}; @@ -64,53 +67,69 @@ impl menu::Item for CompletionItem { let kind = match self { CompletionItem::Lsp(LspCompletionItem { item, .. }) => match item.kind { - Some(lsp::CompletionItemKind::TEXT) => "text", - Some(lsp::CompletionItemKind::METHOD) => "method", - Some(lsp::CompletionItemKind::FUNCTION) => "function", - Some(lsp::CompletionItemKind::CONSTRUCTOR) => "constructor", - Some(lsp::CompletionItemKind::FIELD) => "field", - Some(lsp::CompletionItemKind::VARIABLE) => "variable", - Some(lsp::CompletionItemKind::CLASS) => "class", - Some(lsp::CompletionItemKind::INTERFACE) => "interface", - Some(lsp::CompletionItemKind::MODULE) => "module", - Some(lsp::CompletionItemKind::PROPERTY) => "property", - Some(lsp::CompletionItemKind::UNIT) => "unit", - Some(lsp::CompletionItemKind::VALUE) => "value", - Some(lsp::CompletionItemKind::ENUM) => "enum", - Some(lsp::CompletionItemKind::KEYWORD) => "keyword", - Some(lsp::CompletionItemKind::SNIPPET) => "snippet", - Some(lsp::CompletionItemKind::COLOR) => "color", - Some(lsp::CompletionItemKind::FILE) => "file", - Some(lsp::CompletionItemKind::REFERENCE) => "reference", - Some(lsp::CompletionItemKind::FOLDER) => "folder", - Some(lsp::CompletionItemKind::ENUM_MEMBER) => "enum_member", - Some(lsp::CompletionItemKind::CONSTANT) => "constant", - Some(lsp::CompletionItemKind::STRUCT) => "struct", - Some(lsp::CompletionItemKind::EVENT) => "event", - Some(lsp::CompletionItemKind::OPERATOR) => "operator", - Some(lsp::CompletionItemKind::TYPE_PARAMETER) => "type_param", + Some(lsp::CompletionItemKind::TEXT) => "text".into(), + Some(lsp::CompletionItemKind::METHOD) => "method".into(), + Some(lsp::CompletionItemKind::FUNCTION) => "function".into(), + Some(lsp::CompletionItemKind::CONSTRUCTOR) => "constructor".into(), + Some(lsp::CompletionItemKind::FIELD) => "field".into(), + Some(lsp::CompletionItemKind::VARIABLE) => "variable".into(), + Some(lsp::CompletionItemKind::CLASS) => "class".into(), + Some(lsp::CompletionItemKind::INTERFACE) => "interface".into(), + Some(lsp::CompletionItemKind::MODULE) => "module".into(), + Some(lsp::CompletionItemKind::PROPERTY) => "property".into(), + Some(lsp::CompletionItemKind::UNIT) => "unit".into(), + Some(lsp::CompletionItemKind::VALUE) => "value".into(), + Some(lsp::CompletionItemKind::ENUM) => "enum".into(), + Some(lsp::CompletionItemKind::KEYWORD) => "keyword".into(), + Some(lsp::CompletionItemKind::SNIPPET) => "snippet".into(), + Some(lsp::CompletionItemKind::COLOR) => item + .documentation + .as_ref() + .and_then(|docs| { + let text = match docs { + lsp::Documentation::String(text) => text, + lsp::Documentation::MarkupContent(lsp::MarkupContent { + value, .. + }) => value, + }; + Color::from_hex(text) + }) + .map_or("color".into(), |color| { + Spans::from(vec![ + Span::raw("color "), + Span::styled("■", Style::default().fg(color)), + ]) + }), + Some(lsp::CompletionItemKind::FILE) => "file".into(), + Some(lsp::CompletionItemKind::REFERENCE) => "reference".into(), + Some(lsp::CompletionItemKind::FOLDER) => "folder".into(), + Some(lsp::CompletionItemKind::ENUM_MEMBER) => "enum_member".into(), + Some(lsp::CompletionItemKind::CONSTANT) => "constant".into(), + Some(lsp::CompletionItemKind::STRUCT) => "struct".into(), + Some(lsp::CompletionItemKind::EVENT) => "event".into(), + Some(lsp::CompletionItemKind::OPERATOR) => "operator".into(), + Some(lsp::CompletionItemKind::TYPE_PARAMETER) => "type_param".into(), Some(kind) => { log::error!("Received unknown completion item kind: {:?}", kind); - "" + "".into() } - None => "", + None => "".into(), }, - CompletionItem::Other(core::CompletionItem { kind, .. }) => kind, + CompletionItem::Other(core::CompletionItem { kind, .. }) => kind.as_ref().into(), }; - menu::Row::new([ - menu::Cell::from(Span::styled( - label, - if deprecated { - Style::default().add_modifier(Modifier::CROSSED_OUT) - } else if kind == "folder" { - *dir_style - } else { - Style::default() - }, - )), - menu::Cell::from(kind), - ]) + let label = Span::styled( + label, + if deprecated { + Style::default().add_modifier(Modifier::CROSSED_OUT) + } else if kind.0[0].content == "folder" { + *dir_style + } else { + Style::default() + }, + ); + + menu::Row::new([menu::Cell::from(label), menu::Cell::from(kind)]) } } diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index a26823b97c03..fcc037ed1254 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -263,6 +263,31 @@ pub enum Color { Indexed(u8), } +impl Color { + /// Creates a `Color` from a hex string + /// + /// # Examples + /// + /// ```rust + /// use helix_view::theme::Color; + /// + /// let color1 = Color::from_hex("#c0ffee").unwrap(); + /// let color2 = Color::Rgb(192, 255, 238); + /// + /// assert_eq!(color1, color2); + /// ``` + pub fn from_hex(hex: &str) -> Option { + if !(hex.starts_with('#') && hex.len() == 7) { + return None; + } + match [1..=2, 3..=4, 5..=6].map(|i| hex.get(i).and_then(|c| u8::from_str_radix(c, 16).ok())) + { + [Some(r), Some(g), Some(b)] => Some(Self::Rgb(r, g, b)), + _ => None, + } + } +} + #[cfg(feature = "term")] impl From for crossterm::style::Color { fn from(color: Color) -> Self {