suggestions is getting more and more strong than ever before

This commit is contained in:
Priec
2025-08-07 16:00:46 +02:00
parent 5b2e0e976f
commit a8de16f66d
5 changed files with 1319 additions and 15 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -67,6 +67,15 @@ pub fn render_canvas_with_highlight<T: CanvasTheme, D: DataProvider>(
let current_field_idx = ui_state.current_field();
let is_edit_mode = matches!(ui_state.mode(), crate::canvas::modes::AppMode::Edit);
// Precompute completion for active field
let active_completion = if ui_state.is_suggestions_active()
&& ui_state.suggestions.active_field == Some(current_field_idx)
{
ui_state.suggestions.completion_text.clone()
} else {
None
};
render_canvas_fields(
f,
area,
@@ -111,6 +120,14 @@ pub fn render_canvas_with_highlight<T: CanvasTheme, D: DataProvider>(
false
}
},
// NEW: provide completion for the active field
|i| {
if i == current_field_idx {
active_completion.clone()
} else {
None
}
},
)
}
@@ -128,7 +145,7 @@ fn convert_selection_to_highlight(selection: &crate::canvas::state::SelectionSta
/// Core canvas field rendering
#[cfg(feature = "gui")]
fn render_canvas_fields<T: CanvasTheme, F1, F2>(
fn render_canvas_fields<T: CanvasTheme, F1, F2, F3>(
f: &mut Frame,
area: Rect,
fields: &[&str],
@@ -141,10 +158,12 @@ fn render_canvas_fields<T: CanvasTheme, F1, F2>(
has_unsaved_changes: bool,
get_display_value: F1,
has_display_override: F2,
get_completion: F3,
) -> Option<Rect>
where
F1: Fn(usize) -> String,
F2: Fn(usize) -> bool,
F3: Fn(usize) -> Option<String>,
{
// Create layout
let columns = Layout::default()
@@ -198,6 +217,7 @@ where
current_cursor_pos,
get_display_value,
has_display_override,
get_completion,
)
}
@@ -229,7 +249,7 @@ fn render_field_labels<T: CanvasTheme>(
/// Render field values with highlighting
#[cfg(feature = "gui")]
fn render_field_values<T: CanvasTheme, F1, F2>(
fn render_field_values<T: CanvasTheme, F1, F2, F3>(
f: &mut Frame,
input_rows: Vec<Rect>,
inputs: &[String],
@@ -239,35 +259,54 @@ fn render_field_values<T: CanvasTheme, F1, F2>(
current_cursor_pos: usize,
get_display_value: F1,
has_display_override: F2,
get_completion: F3,
) -> Option<Rect>
where
F1: Fn(usize) -> String,
F2: Fn(usize) -> bool,
F3: Fn(usize) -> Option<String>,
{
let mut active_field_input_rect = None;
for (i, _input) in inputs.iter().enumerate() {
let is_active = i == *current_field_idx;
let text = get_display_value(i);
let typed_text = get_display_value(i);
// Apply highlighting
let line = apply_highlighting(
&text,
i,
current_field_idx,
current_cursor_pos,
highlight_state,
theme,
is_active,
);
let line = if is_active {
// Compose typed + gray completion for the active field
let normal_style = Style::default().fg(theme.fg());
let gray_style = Style::default().fg(theme.suggestion_gray());
let mut spans: Vec<Span> = Vec::new();
spans.push(Span::styled(typed_text.clone(), normal_style));
if let Some(completion) = get_completion(i) {
if !completion.is_empty() {
spans.push(Span::styled(completion, gray_style));
}
}
Line::from(spans)
} else {
// Non-active fields: keep existing highlighting logic
apply_highlighting(
&typed_text,
i,
current_field_idx,
current_cursor_pos,
highlight_state,
theme,
is_active,
)
};
let input_display = Paragraph::new(line).alignment(Alignment::Left);
f.render_widget(input_display, input_rows[i]);
// Set cursor for active field
// Set cursor for active field at end of typed text (not after completion)
if is_active {
active_field_input_rect = Some(input_rows[i]);
set_cursor_position(f, input_rows[i], &text, current_cursor_pos, has_display_override(i));
set_cursor_position(f, input_rows[i], &typed_text, current_cursor_pos, has_display_override(i));
}
}

View File

@@ -35,6 +35,7 @@ pub struct SuggestionsUIState {
pub(crate) is_loading: bool,
pub(crate) selected_index: Option<usize>,
pub(crate) active_field: Option<usize>,
pub(crate) completion_text: Option<String>,
}
#[derive(Debug, Clone)]
@@ -56,6 +57,7 @@ impl EditorState {
is_loading: false,
selected_index: None,
active_field: None,
completion_text: None,
},
selection: SelectionState::None,
#[cfg(feature = "validation")]
@@ -148,6 +150,7 @@ impl EditorState {
self.suggestions.is_loading = true;
self.suggestions.active_field = Some(field_index);
self.suggestions.selected_index = None;
self.suggestions.completion_text = None;
}
pub(crate) fn deactivate_suggestions(&mut self) {
@@ -155,6 +158,7 @@ impl EditorState {
self.suggestions.is_loading = false;
self.suggestions.active_field = None;
self.suggestions.selected_index = None;
self.suggestions.completion_text = None;
}
}

View File

@@ -14,6 +14,7 @@ pub trait CanvasTheme {
fn highlight(&self) -> Color;
fn highlight_bg(&self) -> Color;
fn warning(&self) -> Color;
fn suggestion_gray(&self) -> Color;
}
@@ -47,4 +48,7 @@ impl CanvasTheme for DefaultCanvasTheme {
fn warning(&self) -> Color {
Color::Red
}
fn suggestion_gray(&self) -> Color {
Color::DarkGray
}
}

View File

@@ -40,6 +40,24 @@ impl<D: DataProvider> FormEditor<D> {
editor
}
/// Compute inline completion for current selection and current text.
fn compute_current_completion(&self) -> Option<String> {
let typed = self.current_text();
let idx = self.ui_state.suggestions.selected_index?;
let sugg = self.suggestions.get(idx)?;
if let Some(rest) = sugg.value_to_store.strip_prefix(typed) {
if !rest.is_empty() {
return Some(rest.to_string());
}
}
None
}
/// Update UI state's completion text from current selection
fn update_inline_completion(&mut self) {
self.ui_state.suggestions.completion_text = self.compute_current_completion();
}
/// Initialize validation configurations from data provider
#[cfg(feature = "validation")]
fn initialize_validation(&mut self) {
@@ -605,6 +623,8 @@ impl<D: DataProvider> FormEditor<D> {
self.ui_state.suggestions.is_loading = false;
if !self.suggestions.is_empty() {
self.ui_state.suggestions.selected_index = Some(0);
// Compute initial inline completion from first suggestion
self.update_inline_completion();
}
Ok(())
@@ -619,6 +639,9 @@ impl<D: DataProvider> FormEditor<D> {
let current = self.ui_state.suggestions.selected_index.unwrap_or(0);
let next = (current + 1) % self.suggestions.len();
self.ui_state.suggestions.selected_index = Some(next);
// Update inline completion to reflect new highlighted item
self.update_inline_completion();
}
/// Apply selected suggestion