diff --git a/book/src/configuration.md b/book/src/configuration.md index 1cc8602adac1..e6801a3443a0 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -43,6 +43,7 @@ hidden = false | `auto-format` | Enable automatic formatting on save. | `true` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` | | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | +| `completion-doc-preview` | Show the documentation of the first item in the completion menu | `false` | | `auto-info` | Whether to display infoboxes | `true` | | `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` | | `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` | @@ -50,9 +51,9 @@ hidden = false ### `[editor.lsp]` Section -| Key | Description | Default | -| --- | ----------- | ------- | -| `display-messages` | Display LSP progress messages below statusline[^1] | `false` | +| Key | Description | Default | +| --- | ----------- | ------- | +| `display-messages` | Display LSP progress messages below statusline[^1] | `false` | [^1]: A progress spinner is always shown in the statusline beside the file path. diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 7f82394ac622..68db35463421 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -449,34 +449,40 @@ pub fn code_action(cx: &mut Context) { return; } - let mut picker = ui::Menu::new(actions, (), move |editor, code_action, event| { - if event != PromptEvent::Validate { - return; - } + let mut picker = ui::Menu::new( + actions, + (), + ui::menu::SortStrategy::Text, + move |editor, code_action, event| { + if event != PromptEvent::Validate { + return; + } - // always present here - let code_action = code_action.unwrap(); + // always present here + let code_action = code_action.unwrap(); - match code_action { - lsp::CodeActionOrCommand::Command(command) => { - log::debug!("code action command: {:?}", command); - execute_lsp_command(editor, command.clone()); - } - lsp::CodeActionOrCommand::CodeAction(code_action) => { - log::debug!("code action: {:?}", code_action); - if let Some(ref workspace_edit) = code_action.edit { - log::debug!("edit: {:?}", workspace_edit); - apply_workspace_edit(editor, offset_encoding, workspace_edit); + match code_action { + lsp::CodeActionOrCommand::Command(command) => { + log::debug!("code action command: {:?}", command); + execute_lsp_command(editor, command.clone()); } - // if code action provides both edit and command first the edit - // should be applied and then the command - if let Some(command) = &code_action.command { - execute_lsp_command(editor, command.clone()); + lsp::CodeActionOrCommand::CodeAction(code_action) => { + log::debug!("code action: {:?}", code_action); + if let Some(ref workspace_edit) = code_action.edit { + log::debug!("edit: {:?}", workspace_edit); + apply_workspace_edit(editor, offset_encoding, workspace_edit); + } + + // if code action provides both edit and command first the edit + // should be applied and then the command + if let Some(command) = &code_action.command { + execute_lsp_command(editor, command.clone()); + } } } - } - }); + }, + ); picker.move_down(); // pre-select the first item let popup = diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index a36374154ac7..4e4adf35b459 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -73,6 +73,10 @@ impl menu::Item for CompletionItem { // .as_str(), ]) } + + fn preselected(&self) -> bool { + self.preselect.unwrap_or(false) + } } /// Wraps a Menu. @@ -92,128 +96,136 @@ impl Completion { start_offset: usize, trigger_offset: usize, ) -> Self { - let menu = Menu::new(items, (), move |editor: &mut Editor, item, event| { - fn item_to_transaction( - doc: &Document, - item: &CompletionItem, - offset_encoding: helix_lsp::OffsetEncoding, - start_offset: usize, - trigger_offset: usize, - ) -> Transaction { - let transaction = if let Some(edit) = &item.text_edit { - let edit = match edit { - lsp::CompletionTextEdit::Edit(edit) => edit.clone(), - lsp::CompletionTextEdit::InsertAndReplace(item) => { - unimplemented!("completion: insert_and_replace {:?}", item) - } + let menu = Menu::new( + items, + (), + menu::SortStrategy::Score, + move |editor: &mut Editor, item, event| { + fn item_to_transaction( + doc: &Document, + item: &CompletionItem, + offset_encoding: helix_lsp::OffsetEncoding, + start_offset: usize, + trigger_offset: usize, + ) -> Transaction { + let transaction = if let Some(edit) = &item.text_edit { + let edit = match edit { + lsp::CompletionTextEdit::Edit(edit) => edit.clone(), + lsp::CompletionTextEdit::InsertAndReplace(item) => { + unimplemented!("completion: insert_and_replace {:?}", item) + } + }; + + util::generate_transaction_from_edits( + doc.text(), + vec![edit], + offset_encoding, // TODO: should probably transcode in Client + ) + } else { + let text = item.insert_text.as_ref().unwrap_or(&item.label); + // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯ + // in these cases we need to check for a common prefix and remove it + let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset)); + let text = text.trim_start_matches::<&str>(&prefix); + Transaction::change( + doc.text(), + vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(), + ) }; - util::generate_transaction_from_edits( - doc.text(), - vec![edit], - offset_encoding, // TODO: should probably transcode in Client - ) - } else { - let text = item.insert_text.as_ref().unwrap_or(&item.label); - // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯ - // in these cases we need to check for a common prefix and remove it - let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset)); - let text = text.trim_start_matches::<&str>(&prefix); - Transaction::change( - doc.text(), - vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(), - ) - }; - - transaction - } - - fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec { - transaction - .changes_iter() - .filter(|(start, end, _)| (*start..=*end).contains(&trigger_offset)) - .collect() - } + transaction + } - let (view, doc) = current!(editor); + fn completion_changes( + transaction: &Transaction, + trigger_offset: usize, + ) -> Vec { + transaction + .changes_iter() + .filter(|(start, end, _)| (*start..=*end).contains(&trigger_offset)) + .collect() + } - // if more text was entered, remove it - doc.restore(view.id); + let (view, doc) = current!(editor); - match event { - PromptEvent::Abort => { - doc.restore(view.id); - editor.last_completion = None; - } - PromptEvent::Update => { - // always present here - let item = item.unwrap(); - - let transaction = item_to_transaction( - doc, - item, - offset_encoding, - start_offset, - trigger_offset, - ); - - // initialize a savepoint - doc.savepoint(); - doc.apply(&transaction, view.id); - - editor.last_completion = Some(CompleteAction { - trigger_offset, - changes: completion_changes(&transaction, trigger_offset), - }); - } - PromptEvent::Validate => { - // always present here - let item = item.unwrap(); - - let transaction = item_to_transaction( - doc, - item, - offset_encoding, - start_offset, - trigger_offset, - ); - - doc.apply(&transaction, view.id); - - editor.last_completion = Some(CompleteAction { - trigger_offset, - changes: completion_changes(&transaction, trigger_offset), - }); - - // apply additional edits, mostly used to auto import unqualified types - let resolved_item = if item - .additional_text_edits - .as_ref() - .map(|edits| !edits.is_empty()) - .unwrap_or(false) - { - None - } else { - Self::resolve_completion_item(doc, item.clone()) - }; + // if more text was entered, remove it + doc.restore(view.id); - if let Some(additional_edits) = resolved_item - .as_ref() - .and_then(|item| item.additional_text_edits.as_ref()) - .or(item.additional_text_edits.as_ref()) - { - if !additional_edits.is_empty() { - let transaction = util::generate_transaction_from_edits( - doc.text(), - additional_edits.clone(), - offset_encoding, // TODO: should probably transcode in Client - ); - doc.apply(&transaction, view.id); + match event { + PromptEvent::Abort => { + doc.restore(view.id); + editor.last_completion = None; + } + PromptEvent::Update => { + // always present here + let item = item.unwrap(); + + let transaction = item_to_transaction( + doc, + item, + offset_encoding, + start_offset, + trigger_offset, + ); + + // initialize a savepoint + doc.savepoint(); + doc.apply(&transaction, view.id); + + editor.last_completion = Some(CompleteAction { + trigger_offset, + changes: completion_changes(&transaction, trigger_offset), + }); + } + PromptEvent::Validate => { + // always present here + let item = item.unwrap(); + + let transaction = item_to_transaction( + doc, + item, + offset_encoding, + start_offset, + trigger_offset, + ); + + doc.apply(&transaction, view.id); + + editor.last_completion = Some(CompleteAction { + trigger_offset, + changes: completion_changes(&transaction, trigger_offset), + }); + + // apply additional edits, mostly used to auto import unqualified types + let resolved_item = if item + .additional_text_edits + .as_ref() + .map(|edits| !edits.is_empty()) + .unwrap_or(false) + { + None + } else { + Self::resolve_completion_item(doc, item.clone()) + }; + + if let Some(additional_edits) = resolved_item + .as_ref() + .and_then(|item| item.additional_text_edits.as_ref()) + .or(item.additional_text_edits.as_ref()) + { + if !additional_edits.is_empty() { + let transaction = util::generate_transaction_from_edits( + doc.text(), + additional_edits.clone(), + offset_encoding, // TODO: should probably transcode in Client + ); + doc.apply(&transaction, view.id); + } } } - } - }; - }); + }; + }, + ); let popup = Popup::new("completion", menu); let mut completion = Self { popup, @@ -311,8 +323,17 @@ impl Component for Completion { fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { self.popup.render(area, surface, cx); - // if we have a selection, render a markdown popup on top/below with info - if let Some(option) = self.popup.contents().selection() { + // If we have a selection or a doc preview, render a markdown popup on top/below with info + let mut option = self.popup.contents().selection(); + if option.is_none() + && cx.editor.config().completion_doc_preview + && !self.popup.contents().is_empty() + { + // The doc preview will show the doc for the first completion item even when not selected + option = self.popup.contents().get_match(0); + } + + if let Some(option) = option { // need to render: // option.detail // --- diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 6bb64139155a..23020e713f5c 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -15,6 +15,11 @@ use fuzzy_matcher::FuzzyMatcher; use helix_view::{graphics::Rect, Editor}; use tui::layout::Constraint; +pub enum SortStrategy { + Text, + Score, +} + pub trait Item { /// Additional editor state that is used for label calculation. type Data; @@ -34,6 +39,10 @@ pub trait Item { fn row(&self, data: &Self::Data) -> Row { Row::new(vec![Cell::from(self.label(data))]) } + + fn preselected(&self) -> bool { + false + } } impl Item for PathBuf { @@ -66,6 +75,7 @@ pub struct Menu { size: (u16, u16), viewport: (u16, u16), recalculate: bool, + sort_strategy: SortStrategy, } impl Menu { @@ -76,6 +86,7 @@ impl Menu { pub fn new( options: Vec, editor_data: ::Data, + sort_strategy: SortStrategy, callback_fn: impl Fn(&mut Editor, Option<&T>, MenuEvent) + 'static, ) -> Self { let mut menu = Self { @@ -90,6 +101,7 @@ impl Menu { size: (0, 0), viewport: (0, 0), recalculate: true, + sort_strategy, }; // TODO: scoring on empty input should just use a fastpath @@ -99,6 +111,9 @@ impl Menu { } pub fn score(&mut self, pattern: &str) { + // dereference pointers here, not inside of the filter_map() loop + let is_pattern_empty = pattern.is_empty(); + // reuse the matches allocation self.matches.clear(); self.matches.extend( @@ -108,15 +123,34 @@ impl Menu { .filter_map(|(index, option)| { let text: String = option.filter_text(&self.editor_data).into(); // TODO: using fuzzy_indices could give us the char idx for match highlighting - self.matcher - .fuzzy_match(&text, pattern) - .map(|score| (index, score)) + + // If prompt pattern is empty, show the preselected item as first option + if is_pattern_empty { + if option.preselected() { + // The LSP server can preselect multiple items, however it doesn't give any preference + // for one over the other, so they all have the same score + Some((index, 1)) + } else { + Some((index, 0)) + } + } else { + self.matcher + .fuzzy_match(&text, pattern) + .map(|score| (index, score)) + } }), ); - // matches.sort_unstable_by_key(|(_, score)| -score); - self.matches.sort_unstable_by_key(|(index, _score)| { - self.options[*index].sort_text(&self.editor_data) - }); + + match self.sort_strategy { + SortStrategy::Text => { + self.matches.sort_unstable_by_key(|(index, _score)| { + self.options[*index].sort_text(&self.editor_data) + }); + } + SortStrategy::Score => { + self.matches.sort_unstable_by_key(|(_, score)| -score); + } + }; // reset cursor position self.cursor = None; @@ -206,12 +240,14 @@ impl Menu { } } + pub fn get_match(&self, index: usize) -> Option<&T> { + self.matches + .get(index) + .map(|(index, _score)| &self.options[*index]) + } + pub fn selection(&self) -> Option<&T> { - self.cursor.and_then(|cursor| { - self.matches - .get(cursor) - .map(|(index, _score)| &self.options[*index]) - }) + self.cursor.and_then(|cursor| self.get_match(cursor)) } pub fn is_empty(&self) -> bool { @@ -221,6 +257,14 @@ impl Menu { pub fn len(&self) -> usize { self.matches.len() } + + pub fn options(&self) -> &[T] { + &self.options + } + + pub fn matches(&self) -> &[(usize, i64)] { + self.matches.as_ref() + } } use super::PromptEvent as MenuEvent; @@ -298,6 +342,7 @@ impl Component for Menu { .try_get("ui.menu") .unwrap_or_else(|| theme.get("ui.text")); let selected = theme.get("ui.menu.selected"); + let highlighted = theme.get("ui.menu.highlighted"); surface.clear_with(area, style); let scroll = self.scroll; @@ -327,7 +372,8 @@ impl Component for Menu { let rows = options.iter().map(|option| option.row(&self.editor_data)); let table = Table::new(rows) .style(style) - .highlight_style(selected) + .selected_style(selected) + .highlighted_style(highlighted) .column_spacing(1) .widths(&self.widths); @@ -339,6 +385,10 @@ impl Component for Menu { &mut TableState { offset: scroll, selected: self.cursor, + highlighted: match cx.editor.config().completion_doc_preview { + true => Some(0), + false => None, + }, }, ); diff --git a/helix-tui/src/widgets/table.rs b/helix-tui/src/widgets/table.rs index eb03704ea05f..b578d6c4b47d 100644 --- a/helix-tui/src/widgets/table.rs +++ b/helix-tui/src/widgets/table.rs @@ -175,7 +175,9 @@ impl<'a> Row<'a> { /// // ...and they can be separated by a fixed spacing. /// .column_spacing(1) /// // If you wish to highlight a row in any specific way when it is selected... -/// .highlight_style(Style::default().add_modifier(Modifier::BOLD)) +/// .selected_style(Style::default().add_modifier(Modifier::BOLD)) +/// // If you wish to highlight a row in any specific way when it is preselected... +/// .highlighted_style(Style::default().add_modifier(Modifier::BOLD)) /// // ...and potentially show a symbol in front of the selection. /// .highlight_symbol(">>"); /// ``` @@ -190,7 +192,9 @@ pub struct Table<'a> { /// Space between each column column_spacing: u16, /// Style used to render the selected row - highlight_style: Style, + selected_style: Style, + /// Style used so render a highlighted row + highlighted_style: Style, /// Symbol in front of the selected rom highlight_symbol: Option<&'a str>, /// Optional header @@ -209,7 +213,8 @@ impl<'a> Table<'a> { style: Style::default(), widths: &[], column_spacing: 1, - highlight_style: Style::default(), + selected_style: Style::default(), + highlighted_style: Style::default(), highlight_symbol: None, header: None, rows: rows.into_iter().collect(), @@ -249,8 +254,13 @@ impl<'a> Table<'a> { self } - pub fn highlight_style(mut self, highlight_style: Style) -> Self { - self.highlight_style = highlight_style; + pub fn selected_style(mut self, selected_style: Style) -> Self { + self.selected_style = selected_style; + self + } + + pub fn highlighted_style(mut self, highlighted_style: Style) -> Self { + self.highlighted_style = highlighted_style; self } @@ -367,6 +377,7 @@ impl<'a> Table<'a> { pub struct TableState { pub offset: usize, pub selected: Option, + pub highlighted: Option, } impl TableState { @@ -463,8 +474,15 @@ impl<'a> Table<'a> { }; buf.set_style(table_row_area, table_row.style); let is_selected = state.selected.map(|s| s == i).unwrap_or(false); + let is_highlighted = match has_selection { + true => false, + false => match state.highlighted { + Some(h) => h == i, + None => false, + }, + }; let table_row_start_col = if has_selection { - let symbol = if is_selected { + let symbol = if is_selected || is_highlighted { highlight_symbol } else { &blank_symbol @@ -490,7 +508,9 @@ impl<'a> Table<'a> { col += *width + self.column_spacing; } if is_selected { - buf.set_style(table_row_area, self.highlight_style); + buf.set_style(table_row_area, self.selected_style); + } else if is_highlighted { + buf.set_style(table_row_area, self.highlighted_style); } } } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 1ed27e996eaf..5233dc05e420 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -163,11 +163,14 @@ pub struct Config { pub indent_guides: IndentGuidesConfig, /// Whether to color modes with different colors. Defaults to `false`. pub color_modes: bool, + /// Whether to show the documentation of the first item in the completion menu. Defaults to false. + pub completion_doc_preview: bool, } #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] pub struct LspConfig { + /// Whether to display LSP status messages below the statusline pub display_messages: bool, } @@ -417,6 +420,7 @@ impl Default for Config { whitespace: WhitespaceConfig::default(), indent_guides: IndentGuidesConfig::default(), color_modes: false, + completion_doc_preview: false, } } } diff --git a/runtime/themes/autumn.toml b/runtime/themes/autumn.toml index d96d32eaa76a..709224b2be36 100644 --- a/runtime/themes/autumn.toml +++ b/runtime/themes/autumn.toml @@ -10,6 +10,7 @@ "ui.background" = { bg = "my_gray0" } "ui.menu" = { fg = "my_white", bg = "my_gray2" } "ui.menu.selected" = { fg = "my_gray2", bg = "my_gray5" } +"ui.menu.highlighted" = { fg = "my_red", bg = "my_gray2" } "ui.linenr" = { fg = "my_gray4", bg = "my_gray2" } "ui.popup" = { bg = "my_gray2" } "ui.window" = { fg = "my_gray4", bg = "my_gray2" } diff --git a/runtime/themes/bogster.toml b/runtime/themes/bogster.toml index 76e24648bef5..20329715509e 100644 --- a/runtime/themes/bogster.toml +++ b/runtime/themes/bogster.toml @@ -62,6 +62,7 @@ "ui.menu" = { fg = "#e5ded6bg", bg = "#232d38" } "ui.menu.selected" = { bg = "#313f4e" } +"ui.menu.highlighted" = { fg = "#dcb659" } "warning" = "#dc7759" "error" = "#dc597f" diff --git a/runtime/themes/dracula.toml b/runtime/themes/dracula.toml index 6cc037b8ebc7..a8502feaabbd 100644 --- a/runtime/themes/dracula.toml +++ b/runtime/themes/dracula.toml @@ -29,6 +29,7 @@ "ui.linenr.selected" = { fg = "foreground" } "ui.menu" = { fg = "foreground", bg = "background_dark" } "ui.menu.selected" = { fg = "cyan", bg = "background_dark" } +"ui.menu.highlighted" = { fg = "cyan" } "ui.popup" = { fg = "foreground", bg = "background_dark" } "ui.selection" = { bg = "secondary_highlight" } "ui.selection.primary" = { bg = "primary_highlight" } diff --git a/runtime/themes/gruvbox.toml b/runtime/themes/gruvbox.toml index 48fb14fa1e96..d287d5712184 100644 --- a/runtime/themes/gruvbox.toml +++ b/runtime/themes/gruvbox.toml @@ -54,6 +54,7 @@ "ui.menu" = { fg = "fg1", bg = "bg2" } "ui.menu.selected" = { fg = "bg2", bg = "blue1", modifiers = ["bold"] } "ui.virtual.whitespace" = "bg2" +"ui.menu.highlighted" = { fg = "green1" } "diagnostic" = { modifiers = ["underlined"] } diff --git a/runtime/themes/ingrid.toml b/runtime/themes/ingrid.toml index 587137045422..366cc7a61a86 100644 --- a/runtime/themes/ingrid.toml +++ b/runtime/themes/ingrid.toml @@ -59,6 +59,7 @@ # "ui.cursor.match" # TODO might want to override this because dimmed is not widely supported "ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" } "ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" } +"ui.menu.highlighted" = { fg = "#D74E50" } "warning" = "#D4A520" "error" = "#D74E50" diff --git a/runtime/themes/monokai.toml b/runtime/themes/monokai.toml index 5a8906154982..3b66e28d9433 100644 --- a/runtime/themes/monokai.toml +++ b/runtime/themes/monokai.toml @@ -61,6 +61,7 @@ "ui.help" = { bg = "widget" } "ui.menu" = { bg = "widget" } "ui.menu.selected" = { bg = "#414339" } +"ui.menu.highlighted" = { fg = "#F92672" } "ui.cursor" = { fg = "cursor", modifiers = ["reversed"] } "ui.cursor.primary" = { fg = "cursor", modifiers = ["reversed"] } diff --git a/runtime/themes/nord.toml b/runtime/themes/nord.toml index f7ef079b0c4d..952aef139714 100644 --- a/runtime/themes/nord.toml +++ b/runtime/themes/nord.toml @@ -6,6 +6,7 @@ "ui.menu" = { fg = "nord6", bg = "#232d38" } "ui.menu.selected" = { fg = "nord8", bg = "nord2" } "ui.virtual.whitespace" = "gray" +"ui.menu.highlighted" = { fg = "nord8" } "info" = "nord8" "hint" = "nord8" diff --git a/runtime/themes/onedark.toml b/runtime/themes/onedark.toml index aec7ba960f02..94221f8bddc5 100644 --- a/runtime/themes/onedark.toml +++ b/runtime/themes/onedark.toml @@ -75,6 +75,7 @@ diagnostic = { modifiers = ["underlined"] } "ui.menu" = { fg = "white", bg = "gray" } "ui.menu.selected" = { fg = "black", bg = "blue" } "ui.menu.scroll" = { fg = "white", bg = "light-gray" } +"ui.menu.highlighted" = { fg = "red" } [palette] diff --git a/runtime/themes/rose_pine.toml b/runtime/themes/rose_pine.toml index fd53abd58cb9..3d73ccbd3a79 100644 --- a/runtime/themes/rose_pine.toml +++ b/runtime/themes/rose_pine.toml @@ -4,6 +4,7 @@ "ui.background" = { bg = "base" } "ui.menu" = { fg = "text", bg = "overlay" } "ui.menu.selected" = { fg = "iris", bg = "surface" } +"ui.menu.highlighted" = { fg = "iris"} "ui.linenr" = {fg = "subtle" } "ui.liner.selected" = "highlightOverlay" "ui.selection" = { bg = "highlight" } diff --git a/runtime/themes/snazzy.toml b/runtime/themes/snazzy.toml index da47fd636d49..1e8fdf5068b4 100644 --- a/runtime/themes/snazzy.toml +++ b/runtime/themes/snazzy.toml @@ -30,6 +30,7 @@ "ui.linenr.selected" = { fg = "foreground" } "ui.menu" = { fg = "foreground", bg = "background_dark" } "ui.menu.selected" = { fg = "cyan", bg = "background_dark" } +"ui.menu.highlighted" = { fg = "cyan" } "ui.popup" = { fg = "foreground", bg = "background_dark" } "ui.selection" = { bg = "secondary_highlight" } "ui.selection.primary" = { bg = "primary_highlight" } diff --git a/theme.toml b/theme.toml index ab7c995a8c1f..e86ddda26b5f 100644 --- a/theme.toml +++ b/theme.toml @@ -71,6 +71,7 @@ label = "honey" "ui.menu" = { fg = "lavender", bg = "revolver" } "ui.menu.selected" = { fg = "revolver", bg = "white" } "ui.menu.scroll" = { fg = "lavender", bg = "comet" } +"ui.menu.highlighted" = { fg = "almond"} diagnostic = { modifiers = ["underlined"] } # "diagnostic.hint" = { fg = "revolver", bg = "lilac" }