From f73430eda48b70de58948758910026acf0a92013 Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 11:38:23 +0300 Subject: [PATCH 1/9] remove doubles from syntax and user completions --- Cargo.toml | 6 +++--- src/completer/mod.rs | 17 ++++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f57c2e2..514c487 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "egui_code_editor" authors = ["Roman Chumak "] -version = "0.2.19" +version = "0.2.20" edition = "2024" license = "MIT" repository = "https://github.com/p4ymak/egui_code_editor" -description = "egui Code Editor widget with numbered lines and syntax highlighting.." +description = "egui Code Editor widget with numbered lines, syntax highlighting and auto-completion.." readme = "README.md" categories = ["gui", "text-editors"] -keywords = ["egui", "GUI", "editor", "syntax", "highlighting"] +keywords = ["egui", "GUI", "editor", "syntax", "highlighting", "completion"] [dependencies] egui = { version = "0.33", optional = true } diff --git a/src/completer/mod.rs b/src/completer/mod.rs index b270e1e..c344813 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -1,5 +1,7 @@ mod trie; +use std::collections::BTreeSet; + use crate::{ColorTheme, Syntax, Token, TokenType, format_token}; use egui::{ Event, Frame, Modifiers, Sense, Stroke, TextBuffer, text_edit::TextEditOutput, @@ -42,7 +44,7 @@ pub struct Completer { trie_syntax: Trie, trie_user: Option, variant_id: usize, - completions: Vec, + completions: BTreeSet, } impl Completer { @@ -75,18 +77,18 @@ impl Completer { return; } - let mut completions_syntax = self.trie_syntax.find_completions(&self.prefix); - completions_syntax.reverse(); - let mut completions_user = self + let completions_syntax = self.trie_syntax.find_completions(&self.prefix); + let completions_user = self .trie_user .as_ref() .map(|t| t.find_completions(&self.prefix)) .unwrap_or_default(); - completions_user.reverse(); - self.completions = [completions_syntax, completions_user].concat(); + self.completions = + BTreeSet::from_iter(completions_syntax.into_iter().chain(completions_user)); if self.completions.is_empty() { return; } + let last = self.completions.len().saturating_sub(1); ctx.input_mut(|i| { if i.consume_key(Modifiers::NONE, egui::Key::Escape) { @@ -106,7 +108,8 @@ impl Completer { } else if i.consume_key(Modifiers::NONE, egui::Key::Tab) { let completion = self .completions - .get(self.variant_id) + .iter() + .nth(self.variant_id) .map(String::from) .unwrap_or_default(); i.events.push(Event::Paste(completion)); From 1c5c89c40df7afb0eb601a75e0d483da155e1218 Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 12:32:45 +0300 Subject: [PATCH 2/9] scroll --- src/completer/mod.rs | 61 ++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/completer/mod.rs b/src/completer/mod.rs index c344813..f955399 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -88,7 +88,6 @@ impl Completer { if self.completions.is_empty() { return; } - let last = self.completions.len().saturating_sub(1); ctx.input_mut(|i| { if i.consume_key(Modifiers::NONE, egui::Key::Escape) { @@ -200,33 +199,39 @@ impl Completer { .show(|ui| { ui.response().sense = Sense::empty(); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - - for (i, completion) in self.completions.iter().enumerate() { - let word = format!("{}{completion}", &self.prefix); - let token_type = match &word { - word if syntax.is_keyword(word) => TokenType::Keyword, - word if syntax.is_special(word) => TokenType::Special, - word if syntax.is_type(word) => TokenType::Type, - _ => TokenType::Literal, - }; - let fmt = format_token(theme, fontsize, token_type); - let colored_text = egui::text::LayoutJob::single_section(word, fmt); - let selected = i == self.variant_id; - ui.add( - egui::Button::new(colored_text) - .sense(Sense::empty()) - .frame(true) - .fill(theme.bg()) - .stroke(if selected { - Stroke::new( - ui.style().visuals.widgets.hovered.bg_stroke.width, - theme.type_color(TokenType::Literal), - ) - } else { - Stroke::NONE - }), - ); - } + egui::ScrollArea::vertical().auto_shrink(true).show_rows( + ui, + 0.0, + 10, + |ui, _| { + for (i, completion) in self.completions.iter().enumerate() { + let word = format!("{}{completion}", &self.prefix); + let token_type = match &word { + word if syntax.is_keyword(word) => TokenType::Keyword, + word if syntax.is_special(word) => TokenType::Special, + word if syntax.is_type(word) => TokenType::Type, + _ => TokenType::Literal, + }; + let fmt = format_token(theme, fontsize, token_type); + let colored_text = egui::text::LayoutJob::single_section(word, fmt); + let selected = i == self.variant_id; + ui.add( + egui::Button::new(colored_text) + .sense(Sense::empty()) + .frame(true) + .fill(theme.bg()) + .stroke(if selected { + Stroke::new( + ui.style().visuals.widgets.hovered.bg_stroke.width, + theme.type_color(TokenType::Literal), + ) + } else { + Stroke::NONE + }), + ); + } + }, + ); }); } } From e6079166b549cc3b0f0833a993ab2787263927d9 Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 12:35:35 +0300 Subject: [PATCH 3/9] scroll --- src/completer/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/completer/mod.rs b/src/completer/mod.rs index f955399..5bed9ad 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -215,7 +215,8 @@ impl Completer { let fmt = format_token(theme, fontsize, token_type); let colored_text = egui::text::LayoutJob::single_section(word, fmt); let selected = i == self.variant_id; - ui.add( + + let button = ui.add( egui::Button::new(colored_text) .sense(Sense::empty()) .frame(true) @@ -229,6 +230,9 @@ impl Completer { Stroke::NONE }), ); + if selected { + button.scroll_to_me(None); + } } }, ); From dd17d028eeac9e55e56f983aaf143589ad2626e3 Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 12:38:24 +0300 Subject: [PATCH 4/9] scroll --- src/completer/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/completer/mod.rs b/src/completer/mod.rs index 5bed9ad..f064368 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -188,6 +188,8 @@ impl Completer { String::new() }; if !(self.prefix.is_empty() || self.completions.is_empty()) { + let count = self.completions.len(); + egui::Popup::new( egui::Id::new("Completer"), ctx.clone(), @@ -201,8 +203,8 @@ impl Completer { ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); egui::ScrollArea::vertical().auto_shrink(true).show_rows( ui, - 0.0, - 10, + fontsize, + count, |ui, _| { for (i, completion) in self.completions.iter().enumerate() { let word = format!("{}{completion}", &self.prefix); From 656e81ff4b3bed1f0d753dd122619fe3b1867c54 Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 12:40:26 +0300 Subject: [PATCH 5/9] scroll --- src/completer/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/completer/mod.rs b/src/completer/mod.rs index f064368..2abdb64 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -188,8 +188,6 @@ impl Completer { String::new() }; if !(self.prefix.is_empty() || self.completions.is_empty()) { - let count = self.completions.len(); - egui::Popup::new( egui::Id::new("Completer"), ctx.clone(), @@ -201,10 +199,13 @@ impl Completer { .show(|ui| { ui.response().sense = Sense::empty(); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); + let row_height = ui.spacing().interact_size.y; + // let count = self.completions.len(); + egui::ScrollArea::vertical().auto_shrink(true).show_rows( ui, - fontsize, - count, + row_height, + 10, |ui, _| { for (i, completion) in self.completions.iter().enumerate() { let word = format!("{}{completion}", &self.prefix); From 63ecec1b965d81fab7826f87e12956359a63c1eb Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 12:41:29 +0300 Subject: [PATCH 6/9] scroll --- src/completer/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/completer/mod.rs b/src/completer/mod.rs index 2abdb64..21359e6 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -200,12 +200,12 @@ impl Completer { ui.response().sense = Sense::empty(); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); let row_height = ui.spacing().interact_size.y; - // let count = self.completions.len(); + let count = self.completions.len().min(10); egui::ScrollArea::vertical().auto_shrink(true).show_rows( ui, row_height, - 10, + count, |ui, _| { for (i, completion) in self.completions.iter().enumerate() { let word = format!("{}{completion}", &self.prefix); From 80b07b8996a4d1e9816580690ae900451faa0e62 Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 12:53:27 +0300 Subject: [PATCH 7/9] scroll --- src/completer/mod.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/completer/mod.rs b/src/completer/mod.rs index 21359e6..37fb3f2 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -199,14 +199,13 @@ impl Completer { .show(|ui| { ui.response().sense = Sense::empty(); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - let row_height = ui.spacing().interact_size.y; - let count = self.completions.len().min(10); + let height = fontsize * 1.5 * self.completions.len().min(10) as f32; + ui.set_height(height); - egui::ScrollArea::vertical().auto_shrink(true).show_rows( - ui, - row_height, - count, - |ui, _| { + egui::ScrollArea::vertical() + .auto_shrink([true, true]) + // .show_rows(ui, row_height, count, |ui, _| { + .show(ui, |ui| { for (i, completion) in self.completions.iter().enumerate() { let word = format!("{}{completion}", &self.prefix); let token_type = match &word { @@ -237,8 +236,7 @@ impl Completer { button.scroll_to_me(None); } } - }, - ); + }); }); } } From f605ae16907c7d90d90c2d67750ddd1a79599d4f Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Wed, 15 Oct 2025 13:35:58 +0300 Subject: [PATCH 8/9] scroll --- src/completer/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/completer/mod.rs b/src/completer/mod.rs index 37fb3f2..306648c 100644 --- a/src/completer/mod.rs +++ b/src/completer/mod.rs @@ -199,12 +199,17 @@ impl Completer { .show(|ui| { ui.response().sense = Sense::empty(); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - let height = fontsize * 1.5 * self.completions.len().min(10) as f32; + let height = (fontsize + + ui.style().visuals.widgets.hovered.bg_stroke.width * 2.0 + + ui.style().spacing.button_padding.y * 2.0 + + ui.style().spacing.item_spacing.y) + * self.completions.len().min(10) as f32 + - ui.style().spacing.item_spacing.y; ui.set_height(height); egui::ScrollArea::vertical() .auto_shrink([true, true]) - // .show_rows(ui, row_height, count, |ui, _| { + .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) .show(ui, |ui| { for (i, completion) in self.completions.iter().enumerate() { let word = format!("{}{completion}", &self.prefix); From 5a102d9d87c95009e0024b6d3eb44734c617b566 Mon Sep 17 00:00:00 2001 From: Roman Chumak Date: Thu, 16 Oct 2025 16:09:41 +0300 Subject: [PATCH 9/9] fix typo --- src/completer/trie.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/completer/trie.rs b/src/completer/trie.rs index ed9eb38..5b768f6 100644 --- a/src/completer/trie.rs +++ b/src/completer/trie.rs @@ -98,10 +98,10 @@ impl Trie { let mut start = " ".to_string(); start.push_str(prefix); let mut part = start.chars().peekable(); - self.find_recursice(&mut part, &mut found); + self.find_recursive(&mut part, &mut found); found } - fn find_recursice<'a>(&'a self, part: &mut Peekable, found: &mut Option<&'a Trie>) { + fn find_recursive<'a>(&'a self, part: &mut Peekable, found: &mut Option<&'a Trie>) { if let Some(c) = part.next() && self.root == c { @@ -110,7 +110,7 @@ impl Trie { } self.leaves .iter() - .for_each(|l| l.find_recursice(&mut part.clone(), found)) + .for_each(|l| l.find_recursive(&mut part.clone(), found)) } } }