cargo clippy ran
This commit is contained in:
@@ -38,7 +38,7 @@ use ratatui::{
|
||||
use canvas::{
|
||||
canvas::{
|
||||
gui::render_canvas_default,
|
||||
modes::{AppMode, ModeManager, HighlightState},
|
||||
modes::AppMode,
|
||||
CursorManager, // This import only exists when cursor-style feature is enabled
|
||||
},
|
||||
DataProvider, FormEditor,
|
||||
@@ -205,7 +205,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌫ Deleted character backward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn delete_forward(&mut self) -> anyhow::Result<()> {
|
||||
@@ -214,7 +214,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌦ Deleted character forward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === MODE TRANSITIONS WITH AUTOMATIC CURSOR MANAGEMENT ===
|
||||
@@ -240,7 +240,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
if result.is_ok() {
|
||||
self.has_unsaved_changes = true;
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === MANUAL CURSOR OVERRIDE DEMONSTRATION ===
|
||||
@@ -429,13 +429,13 @@ fn handle_key_press(
|
||||
|
||||
(AppMode::ReadOnly, KeyCode::Char('o'), _) => {
|
||||
if let Err(e) = editor.open_line_below() {
|
||||
editor.set_debug_message(format!("Error opening line below: {}", e));
|
||||
editor.set_debug_message(format!("Error opening line below: {e}"));
|
||||
}
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('O'), _) => {
|
||||
if let Err(e) = editor.open_line_above() {
|
||||
editor.set_debug_message(format!("Error opening line above: {}", e));
|
||||
editor.set_debug_message(format!("Error opening line above: {e}"));
|
||||
}
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
@@ -694,8 +694,7 @@ fn handle_key_press(
|
||||
editor.set_debug_message("Invalid command sequence".to_string());
|
||||
} else {
|
||||
editor.set_debug_message(format!(
|
||||
"Unhandled: {:?} + {:?} in {:?} mode",
|
||||
key, modifiers, mode
|
||||
"Unhandled: {key:?} + {modifiers:?} in {mode:?} mode"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -719,7 +718,7 @@ fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -858,7 +857,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🎯 Cursor automatically reset to default!");
|
||||
|
||||
@@ -133,7 +133,7 @@ impl ComputedProvider for InvoiceCalculator {
|
||||
if qty == 0.0 || price == 0.0 {
|
||||
"".to_string() // Show empty if no meaningful calculation
|
||||
} else {
|
||||
format!("{:.2}", subtotal)
|
||||
format!("{subtotal:.2}")
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
@@ -147,7 +147,7 @@ impl ComputedProvider for InvoiceCalculator {
|
||||
if subtotal == 0.0 || tax_rate == 0.0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!("{:.2}", tax_amount)
|
||||
format!("{tax_amount:.2}")
|
||||
}
|
||||
}
|
||||
6 => {
|
||||
@@ -162,7 +162,7 @@ impl ComputedProvider for InvoiceCalculator {
|
||||
} else {
|
||||
let tax_amount = subtotal * (tax_rate / 100.0);
|
||||
let total = subtotal + tax_amount;
|
||||
format!("{:.2}", total)
|
||||
format!("{total:.2}")
|
||||
}
|
||||
}
|
||||
_ => "".to_string(),
|
||||
@@ -170,7 +170,7 @@ impl ComputedProvider for InvoiceCalculator {
|
||||
}
|
||||
|
||||
fn handles_field(&self, field_index: usize) -> bool {
|
||||
matches!(field_index, 4 | 5 | 6) // Subtotal, Tax Amount, Total
|
||||
matches!(field_index, 4..=6) // Subtotal, Tax Amount, Total
|
||||
}
|
||||
|
||||
fn field_dependencies(&self, field_index: usize) -> Vec<usize> {
|
||||
@@ -244,13 +244,13 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
|
||||
let mut parts = Vec::new();
|
||||
if !subtotal.is_empty() {
|
||||
parts.push(format!("Subtotal=${}", subtotal));
|
||||
parts.push(format!("Subtotal=${subtotal}"));
|
||||
}
|
||||
if !tax.is_empty() {
|
||||
parts.push(format!("Tax=${}", tax));
|
||||
parts.push(format!("Tax=${tax}"));
|
||||
}
|
||||
if !total.is_empty() {
|
||||
parts.push(format!("Total=${}", total));
|
||||
parts.push(format!("Total=${total}"));
|
||||
}
|
||||
|
||||
if !parts.is_empty() {
|
||||
@@ -268,7 +268,7 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
let current_field = self.editor.current_field();
|
||||
let result = self.editor.insert_char(ch);
|
||||
|
||||
if result.is_ok() && matches!(current_field, 1 | 2 | 3) {
|
||||
if result.is_ok() && matches!(current_field, 1..=3) {
|
||||
self.editor.on_field_changed(&mut self.calculator, current_field);
|
||||
self.update_computed_fields();
|
||||
}
|
||||
@@ -280,7 +280,7 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
let current_field = self.editor.current_field();
|
||||
let result = self.editor.delete_backward();
|
||||
|
||||
if result.is_ok() && matches!(current_field, 1 | 2 | 3) {
|
||||
if result.is_ok() && matches!(current_field, 1..=3) {
|
||||
self.editor.on_field_changed(&mut self.calculator, current_field);
|
||||
self.update_computed_fields();
|
||||
}
|
||||
@@ -292,7 +292,7 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
let current_field = self.editor.current_field();
|
||||
let result = self.editor.delete_forward();
|
||||
|
||||
if result.is_ok() && matches!(current_field, 1 | 2 | 3) {
|
||||
if result.is_ok() && matches!(current_field, 1..=3) {
|
||||
self.editor.on_field_changed(&mut self.calculator, current_field);
|
||||
self.update_computed_fields();
|
||||
}
|
||||
@@ -312,7 +312,7 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
} else {
|
||||
"editable"
|
||||
};
|
||||
self.debug_message = format!("→ {} - {} field", field_name, field_type);
|
||||
self.debug_message = format!("→ {field_name} - {field_type} field");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
} else {
|
||||
"editable"
|
||||
};
|
||||
self.debug_message = format!("← {} - {} field", field_name, field_type);
|
||||
self.debug_message = format!("← {field_name} - {field_type} field");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,15 +339,14 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
if self.editor.data_provider().is_computed_field(current) || self.is_computed_field(current) {
|
||||
let field_name = self.editor.data_provider().field_name(current);
|
||||
self.debug_message = format!(
|
||||
"🚫 {} is computed (read-only) - Press Tab to move to editable fields",
|
||||
field_name
|
||||
"🚫 {field_name} is computed (read-only) - Press Tab to move to editable fields"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
self.editor.enter_edit_mode();
|
||||
let field_name = self.editor.data_provider().field_name(current);
|
||||
self.debug_message = format!("✏️ Editing {} - Type to see calculations update", field_name);
|
||||
self.debug_message = format!("✏️ Editing {field_name} - Type to see calculations update");
|
||||
}
|
||||
|
||||
fn enter_append_mode(&mut self) {
|
||||
@@ -356,22 +355,21 @@ impl<D: DataProvider> ComputedFieldsEditor<D> {
|
||||
if self.editor.data_provider().is_computed_field(current) || self.is_computed_field(current) {
|
||||
let field_name = self.editor.data_provider().field_name(current);
|
||||
self.debug_message = format!(
|
||||
"🚫 {} is computed (read-only) - Press Tab to move to editable fields",
|
||||
field_name
|
||||
"🚫 {field_name} is computed (read-only) - Press Tab to move to editable fields"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
self.editor.enter_append_mode();
|
||||
let field_name = self.editor.data_provider().field_name(current);
|
||||
self.debug_message = format!("✏️ Appending to {} - Type to see calculations", field_name);
|
||||
self.debug_message = format!("✏️ Appending to {field_name} - Type to see calculations");
|
||||
}
|
||||
|
||||
fn exit_edit_mode(&mut self) {
|
||||
let current_field = self.editor.current_field();
|
||||
self.editor.exit_edit_mode();
|
||||
|
||||
if matches!(current_field, 1 | 2 | 3) {
|
||||
if matches!(current_field, 1..=3) {
|
||||
self.editor.on_field_changed(&mut self.calculator, current_field);
|
||||
self.update_computed_fields();
|
||||
}
|
||||
@@ -503,7 +501,7 @@ fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.debug_message = format!("Error: {}", e);
|
||||
editor.debug_message = format!("Error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -615,7 +613,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("💰 Demo completed! Computed fields should have updated in real-time!");
|
||||
|
||||
@@ -1,724 +0,0 @@
|
||||
// examples/full_canvas_demo.rs
|
||||
//! Demonstrates the FULL potential of the canvas library using the native API
|
||||
|
||||
use std::io;
|
||||
use crossterm::{
|
||||
cursor::SetCursorStyle,
|
||||
event::{
|
||||
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyModifiers,
|
||||
},
|
||||
execute,
|
||||
terminal::{
|
||||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
|
||||
},
|
||||
};
|
||||
use ratatui::{
|
||||
backend::{Backend, CrosstermBackend},
|
||||
layout::{Constraint, Direction, Layout},
|
||||
style::{Color, Style},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
Frame, Terminal,
|
||||
};
|
||||
|
||||
use canvas::{
|
||||
canvas::{
|
||||
gui::render_canvas_default,
|
||||
modes::{AppMode, ModeManager, HighlightState},
|
||||
},
|
||||
DataProvider, FormEditor,
|
||||
};
|
||||
|
||||
/// Update cursor style based on current AppMode
|
||||
fn update_cursor_for_mode(mode: AppMode) -> io::Result<()> {
|
||||
let style = match mode {
|
||||
AppMode::Edit => SetCursorStyle::SteadyBar, // Thin line for insert mode
|
||||
AppMode::ReadOnly => SetCursorStyle::SteadyBlock, // Block for normal mode
|
||||
AppMode::Highlight => SetCursorStyle::BlinkingBlock, // Blinking block for visual mode
|
||||
AppMode::General => SetCursorStyle::SteadyBlock, // Block for general mode
|
||||
AppMode::Command => SetCursorStyle::SteadyUnderScore, // Underscore for command mode
|
||||
};
|
||||
|
||||
execute!(io::stdout(), style)
|
||||
}
|
||||
|
||||
// Enhanced FormEditor that adds visual mode and status tracking
|
||||
struct EnhancedFormEditor<D: DataProvider> {
|
||||
editor: FormEditor<D>,
|
||||
highlight_state: HighlightState,
|
||||
has_unsaved_changes: bool,
|
||||
debug_message: String,
|
||||
command_buffer: String, // For multi-key vim commands like "gg"
|
||||
}
|
||||
|
||||
impl<D: DataProvider> EnhancedFormEditor<D> {
|
||||
fn new(data_provider: D) -> Self {
|
||||
Self {
|
||||
editor: FormEditor::new(data_provider),
|
||||
highlight_state: HighlightState::Off,
|
||||
has_unsaved_changes: false,
|
||||
debug_message: "Full Canvas Demo - All features enabled".to_string(),
|
||||
command_buffer: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// === COMMAND BUFFER HANDLING ===
|
||||
|
||||
fn clear_command_buffer(&mut self) {
|
||||
self.command_buffer.clear();
|
||||
}
|
||||
|
||||
fn add_to_command_buffer(&mut self, ch: char) {
|
||||
self.command_buffer.push(ch);
|
||||
}
|
||||
|
||||
fn get_command_buffer(&self) -> &str {
|
||||
&self.command_buffer
|
||||
}
|
||||
|
||||
fn has_pending_command(&self) -> bool {
|
||||
!self.command_buffer.is_empty()
|
||||
}
|
||||
|
||||
// === VISUAL/HIGHLIGHT MODE SUPPORT ===
|
||||
|
||||
fn enter_visual_mode(&mut self) {
|
||||
if ModeManager::can_enter_highlight_mode(self.editor.mode()) {
|
||||
self.editor.set_mode(AppMode::Highlight);
|
||||
self.highlight_state = HighlightState::Characterwise {
|
||||
anchor: (
|
||||
self.editor.current_field(),
|
||||
self.editor.cursor_position(),
|
||||
),
|
||||
};
|
||||
self.debug_message = "-- VISUAL --".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_visual_line_mode(&mut self) {
|
||||
if ModeManager::can_enter_highlight_mode(self.editor.mode()) {
|
||||
self.editor.set_mode(AppMode::Highlight);
|
||||
self.highlight_state =
|
||||
HighlightState::Linewise { anchor_line: self.editor.current_field() };
|
||||
self.debug_message = "-- VISUAL LINE --".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_visual_mode(&mut self) {
|
||||
self.highlight_state = HighlightState::Off;
|
||||
if self.editor.mode() == AppMode::Highlight {
|
||||
self.editor.set_mode(AppMode::ReadOnly);
|
||||
self.debug_message = "Visual mode exited".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_visual_selection(&mut self) {
|
||||
if self.editor.mode() == AppMode::Highlight {
|
||||
match &self.highlight_state {
|
||||
HighlightState::Characterwise { anchor: _ } => {
|
||||
self.debug_message = format!(
|
||||
"Visual selection: char {} in field {}",
|
||||
self.editor.cursor_position(),
|
||||
self.editor.current_field()
|
||||
);
|
||||
}
|
||||
HighlightState::Linewise { anchor_line: _ } => {
|
||||
self.debug_message = format!(
|
||||
"Visual line selection: field {}",
|
||||
self.editor.current_field()
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === ENHANCED MOVEMENT WITH VISUAL UPDATES ===
|
||||
|
||||
fn move_left(&mut self) {
|
||||
self.editor.move_left();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_right(&mut self) {
|
||||
self.editor.move_right();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_up(&mut self) {
|
||||
self.editor.move_up();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_down(&mut self) {
|
||||
self.editor.move_down();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_word_next(&mut self) {
|
||||
self.editor.move_word_next();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_word_prev(&mut self) {
|
||||
self.editor.move_word_prev();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_word_end(&mut self) {
|
||||
self.editor.move_word_end();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_word_end_prev(&mut self) {
|
||||
self.editor.move_word_end_prev();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_line_start(&mut self) {
|
||||
self.editor.move_line_start();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_line_end(&mut self) {
|
||||
self.editor.move_line_end();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_first_line(&mut self) {
|
||||
self.editor.move_first_line();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn move_last_line(&mut self) {
|
||||
self.editor.move_last_line();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn prev_field(&mut self) {
|
||||
self.editor.prev_field();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
fn next_field(&mut self) {
|
||||
self.editor.next_field();
|
||||
self.update_visual_selection();
|
||||
}
|
||||
|
||||
// === DELETE OPERATIONS ===
|
||||
|
||||
fn delete_backward(&mut self) -> anyhow::Result<()> {
|
||||
let result = self.editor.delete_backward();
|
||||
if result.is_ok() {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "Deleted character backward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
fn delete_forward(&mut self) -> anyhow::Result<()> {
|
||||
let result = self.editor.delete_forward();
|
||||
if result.is_ok() {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "Deleted character forward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
// === MODE TRANSITIONS ===
|
||||
|
||||
fn enter_edit_mode(&mut self) {
|
||||
self.editor.enter_edit_mode();
|
||||
self.debug_message = "-- INSERT --".to_string();
|
||||
}
|
||||
|
||||
fn exit_edit_mode(&mut self) {
|
||||
self.editor.exit_edit_mode();
|
||||
self.exit_visual_mode();
|
||||
self.debug_message = "".to_string();
|
||||
}
|
||||
|
||||
fn insert_char(&mut self, ch: char) -> anyhow::Result<()> {
|
||||
let result = self.editor.insert_char(ch);
|
||||
if result.is_ok() {
|
||||
self.has_unsaved_changes = true;
|
||||
}
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
// === DELEGATE TO ORIGINAL EDITOR ===
|
||||
|
||||
fn current_field(&self) -> usize {
|
||||
self.editor.current_field()
|
||||
}
|
||||
|
||||
fn cursor_position(&self) -> usize {
|
||||
self.editor.cursor_position()
|
||||
}
|
||||
|
||||
fn mode(&self) -> AppMode {
|
||||
self.editor.mode()
|
||||
}
|
||||
|
||||
fn current_text(&self) -> &str {
|
||||
self.editor.current_text()
|
||||
}
|
||||
|
||||
fn data_provider(&self) -> &D {
|
||||
self.editor.data_provider()
|
||||
}
|
||||
|
||||
fn ui_state(&self) -> &canvas::EditorState {
|
||||
self.editor.ui_state()
|
||||
}
|
||||
|
||||
fn set_mode(&mut self, mode: AppMode) {
|
||||
self.editor.set_mode(mode);
|
||||
if mode != AppMode::Highlight {
|
||||
self.exit_visual_mode();
|
||||
}
|
||||
}
|
||||
|
||||
// === STATUS AND DEBUG ===
|
||||
|
||||
fn set_debug_message(&mut self, msg: String) {
|
||||
self.debug_message = msg;
|
||||
}
|
||||
|
||||
fn debug_message(&self) -> &str {
|
||||
&self.debug_message
|
||||
}
|
||||
|
||||
fn highlight_state(&self) -> &HighlightState {
|
||||
&self.highlight_state
|
||||
}
|
||||
|
||||
fn has_unsaved_changes(&self) -> bool {
|
||||
self.has_unsaved_changes
|
||||
}
|
||||
}
|
||||
|
||||
// Demo form data with interesting text for word movement
|
||||
struct FullDemoData {
|
||||
fields: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl FullDemoData {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
fields: vec![
|
||||
("Name".to_string(), "John-Paul McDonald".to_string()),
|
||||
(
|
||||
"Email".to_string(),
|
||||
"user@example-domain.com".to_string(),
|
||||
),
|
||||
("Phone".to_string(), "+1 (555) 123-4567".to_string()),
|
||||
("Address".to_string(), "123 Main St, Apt 4B".to_string()),
|
||||
(
|
||||
"Tags".to_string(),
|
||||
"urgent,important,follow-up".to_string(),
|
||||
),
|
||||
(
|
||||
"Notes".to_string(),
|
||||
"This is a sample note with multiple words, punctuation! And symbols @#$"
|
||||
.to_string(),
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataProvider for FullDemoData {
|
||||
fn field_count(&self) -> usize {
|
||||
self.fields.len()
|
||||
}
|
||||
|
||||
fn field_name(&self, index: usize) -> &str {
|
||||
&self.fields[index].0
|
||||
}
|
||||
|
||||
fn field_value(&self, index: usize) -> &str {
|
||||
&self.fields[index].1
|
||||
}
|
||||
|
||||
fn set_field_value(&mut self, index: usize, value: String) {
|
||||
self.fields[index].1 = value;
|
||||
}
|
||||
|
||||
fn supports_suggestions(&self, _field_index: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn display_value(&self, _index: usize) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Full vim-like key handling using the native FormEditor API
|
||||
fn handle_key_press(
|
||||
key: KeyCode,
|
||||
modifiers: KeyModifiers,
|
||||
editor: &mut EnhancedFormEditor<FullDemoData>,
|
||||
) -> anyhow::Result<bool> {
|
||||
let old_mode = editor.mode(); // Store mode before processing
|
||||
|
||||
// Quit handling
|
||||
if (key == KeyCode::Char('q') && modifiers.contains(KeyModifiers::CONTROL))
|
||||
|| (key == KeyCode::Char('c') && modifiers.contains(KeyModifiers::CONTROL))
|
||||
|| key == KeyCode::F(10)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
match (old_mode, key, modifiers) {
|
||||
// === MODE TRANSITIONS ===
|
||||
(AppMode::ReadOnly, KeyCode::Char('i'), _) => {
|
||||
editor.enter_edit_mode();
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('a'), _) => {
|
||||
editor.move_right(); // Move after current character
|
||||
editor.enter_edit_mode();
|
||||
editor.set_debug_message("-- INSERT -- (append)".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('A'), _) => {
|
||||
editor.move_line_end();
|
||||
editor.enter_edit_mode();
|
||||
editor.set_debug_message("-- INSERT -- (end of line)".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('o'), _) => {
|
||||
editor.move_line_end();
|
||||
editor.enter_edit_mode();
|
||||
editor.set_debug_message("-- INSERT -- (open line)".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('v'), _) => {
|
||||
editor.enter_visual_mode();
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('V'), _) => {
|
||||
editor.enter_visual_line_mode();
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(_, KeyCode::Esc, _) => {
|
||||
editor.exit_edit_mode();
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
|
||||
// === MOVEMENT: VIM-STYLE NAVIGATION ===
|
||||
|
||||
// Basic movement (hjkl and arrows)
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('h'), _)
|
||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Left, _) => {
|
||||
editor.move_left();
|
||||
editor.set_debug_message("← left".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('l'), _)
|
||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Right, _) => {
|
||||
editor.move_right();
|
||||
editor.set_debug_message("→ right".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('j'), _)
|
||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Down, _) => {
|
||||
editor.move_down();
|
||||
editor.set_debug_message("↓ next field".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('k'), _)
|
||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Up, _) => {
|
||||
editor.move_up();
|
||||
editor.set_debug_message("↑ previous field".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
|
||||
// Word movement - Full vim word navigation
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('w'), _) => {
|
||||
editor.move_word_next();
|
||||
editor.set_debug_message("w: next word start".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('b'), _) => {
|
||||
editor.move_word_prev();
|
||||
editor.set_debug_message("b: previous word start".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('e'), _) => {
|
||||
editor.move_word_end();
|
||||
editor.set_debug_message("e: word end".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('W'), _) => {
|
||||
editor.move_word_end_prev();
|
||||
editor.set_debug_message("W: previous word end".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
|
||||
// Line movement
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('0'), _)
|
||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Home, _) => {
|
||||
editor.move_line_start();
|
||||
editor.set_debug_message("0: line start".to_string());
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('$'), _)
|
||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::End, _) => {
|
||||
editor.move_line_end();
|
||||
editor.set_debug_message("$: line end".to_string());
|
||||
}
|
||||
|
||||
// Field/document movement
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('g'), _) => {
|
||||
if editor.get_command_buffer() == "g" {
|
||||
// Second 'g' - execute "gg" command
|
||||
editor.move_first_line();
|
||||
editor.set_debug_message("gg: first field".to_string());
|
||||
editor.clear_command_buffer();
|
||||
} else {
|
||||
// First 'g' - start command buffer
|
||||
editor.clear_command_buffer();
|
||||
editor.add_to_command_buffer('g');
|
||||
editor.set_debug_message("g".to_string());
|
||||
}
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('G'), _) => {
|
||||
editor.move_last_line();
|
||||
editor.set_debug_message("G: last field".to_string());
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
|
||||
// === EDIT MODE MOVEMENT ===
|
||||
(AppMode::Edit, KeyCode::Left, m) if m.contains(KeyModifiers::CONTROL) => {
|
||||
editor.move_word_prev();
|
||||
editor.set_debug_message("Ctrl+← word back".to_string());
|
||||
}
|
||||
(AppMode::Edit, KeyCode::Right, m) if m.contains(KeyModifiers::CONTROL) => {
|
||||
editor.move_word_next();
|
||||
editor.set_debug_message("Ctrl+→ word forward".to_string());
|
||||
}
|
||||
(AppMode::Edit, KeyCode::Left, _) => {
|
||||
editor.move_left();
|
||||
}
|
||||
(AppMode::Edit, KeyCode::Right, _) => {
|
||||
editor.move_right();
|
||||
}
|
||||
(AppMode::Edit, KeyCode::Up, _) => {
|
||||
editor.move_up();
|
||||
}
|
||||
(AppMode::Edit, KeyCode::Down, _) => {
|
||||
editor.move_down();
|
||||
}
|
||||
(AppMode::Edit, KeyCode::Home, _) => {
|
||||
editor.move_line_start();
|
||||
}
|
||||
(AppMode::Edit, KeyCode::End, _) => {
|
||||
editor.move_line_end();
|
||||
}
|
||||
|
||||
// === DELETE OPERATIONS ===
|
||||
(AppMode::Edit, KeyCode::Backspace, _) => {
|
||||
editor.delete_backward()?;
|
||||
}
|
||||
(AppMode::Edit, KeyCode::Delete, _) => {
|
||||
editor.delete_forward()?;
|
||||
}
|
||||
|
||||
// Delete operations in normal mode (vim x)
|
||||
(AppMode::ReadOnly, KeyCode::Char('x'), _) => {
|
||||
editor.delete_forward()?;
|
||||
editor.set_debug_message("x: deleted character".to_string());
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('X'), _) => {
|
||||
editor.delete_backward()?;
|
||||
editor.set_debug_message("X: deleted character backward".to_string());
|
||||
}
|
||||
|
||||
// === TAB NAVIGATION ===
|
||||
(_, KeyCode::Tab, _) => {
|
||||
editor.next_field();
|
||||
editor.set_debug_message("Tab: next field".to_string());
|
||||
}
|
||||
(_, KeyCode::BackTab, _) => {
|
||||
editor.prev_field();
|
||||
editor.set_debug_message("Shift+Tab: previous field".to_string());
|
||||
}
|
||||
|
||||
// === CHARACTER INPUT ===
|
||||
(AppMode::Edit, KeyCode::Char(c), m) if !m.contains(KeyModifiers::CONTROL) => {
|
||||
editor.insert_char(c)?;
|
||||
}
|
||||
|
||||
// === DEBUG/INFO COMMANDS ===
|
||||
(AppMode::ReadOnly, KeyCode::Char('?'), _) => {
|
||||
editor.set_debug_message(format!(
|
||||
"Field {}/{}, Pos {}, Mode: {:?}",
|
||||
editor.current_field() + 1,
|
||||
editor.data_provider().field_count(),
|
||||
editor.cursor_position(),
|
||||
editor.mode()
|
||||
));
|
||||
}
|
||||
|
||||
_ => {
|
||||
// If we have a pending command and this key doesn't complete it, clear the buffer
|
||||
if editor.has_pending_command() {
|
||||
editor.clear_command_buffer();
|
||||
editor.set_debug_message("Invalid command sequence".to_string());
|
||||
} else {
|
||||
editor.set_debug_message(format!(
|
||||
"Unhandled: {:?} + {:?} in {:?} mode",
|
||||
key, modifiers, old_mode
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update cursor if mode changed
|
||||
let new_mode = editor.mode();
|
||||
if old_mode != new_mode {
|
||||
update_cursor_for_mode(new_mode)?;
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn run_app<B: Backend>(
|
||||
terminal: &mut Terminal<B>,
|
||||
mut editor: EnhancedFormEditor<FullDemoData>,
|
||||
) -> io::Result<()> {
|
||||
loop {
|
||||
terminal.draw(|f| ui(f, &editor))?;
|
||||
|
||||
if let Event::Key(key) = event::read()? {
|
||||
match handle_key_press(key.code, key.modifiers, &mut editor) {
|
||||
Ok(should_continue) => {
|
||||
if !should_continue {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ui(f: &mut Frame, editor: &EnhancedFormEditor<FullDemoData>) {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(8), Constraint::Length(8)])
|
||||
.split(f.area());
|
||||
|
||||
render_enhanced_canvas(f, chunks[0], editor);
|
||||
render_status_and_help(f, chunks[1], editor);
|
||||
}
|
||||
|
||||
fn render_enhanced_canvas(
|
||||
f: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
editor: &EnhancedFormEditor<FullDemoData>,
|
||||
) {
|
||||
render_canvas_default(f, area, &editor.editor);
|
||||
}
|
||||
|
||||
fn render_status_and_help(
|
||||
f: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
editor: &EnhancedFormEditor<FullDemoData>,
|
||||
) {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Length(3), Constraint::Length(5)])
|
||||
.split(area);
|
||||
|
||||
// Status bar
|
||||
let mode_text = match editor.mode() {
|
||||
AppMode::Edit => "INSERT",
|
||||
AppMode::ReadOnly => "NORMAL",
|
||||
AppMode::Highlight => match editor.highlight_state() {
|
||||
HighlightState::Characterwise { .. } => "VISUAL",
|
||||
HighlightState::Linewise { .. } => "VISUAL LINE",
|
||||
_ => "VISUAL",
|
||||
},
|
||||
_ => "NORMAL",
|
||||
};
|
||||
|
||||
let status_text = if editor.has_pending_command() {
|
||||
format!("-- {} -- {} [{}]", mode_text, editor.debug_message(), editor.get_command_buffer())
|
||||
} else if editor.has_unsaved_changes() {
|
||||
format!("-- {} -- [Modified] {}", mode_text, editor.debug_message())
|
||||
} else {
|
||||
format!("-- {} -- {}", mode_text, editor.debug_message())
|
||||
};
|
||||
|
||||
let status = Paragraph::new(Line::from(Span::raw(status_text)))
|
||||
.block(Block::default().borders(Borders::ALL).title("Status"));
|
||||
|
||||
f.render_widget(status, chunks[0]);
|
||||
|
||||
// Help text
|
||||
let help_text = match editor.mode() {
|
||||
AppMode::ReadOnly => {
|
||||
if editor.has_pending_command() {
|
||||
match editor.get_command_buffer() {
|
||||
"g" => "Press 'g' again for first field, or any other key to cancel",
|
||||
_ => "Pending command... (Esc to cancel)"
|
||||
}
|
||||
} else {
|
||||
"Normal: hjkl/arrows=move, w/b/e=words, 0/$=line, gg/G=first/last, i/a/A=insert, v/V=visual, x/X=delete, ?=info"
|
||||
}
|
||||
}
|
||||
AppMode::Edit => {
|
||||
"Insert: arrows=move, Ctrl+arrows=words, Backspace/Del=delete, Esc=normal, Tab/Shift+Tab=fields"
|
||||
}
|
||||
AppMode::Highlight => {
|
||||
"Visual: hjkl/arrows=extend selection, w/b/e=word selection, Esc=normal"
|
||||
}
|
||||
_ => "Press ? for help"
|
||||
};
|
||||
|
||||
let help = Paragraph::new(Line::from(Span::raw(help_text)))
|
||||
.block(Block::default().borders(Borders::ALL).title("Commands"))
|
||||
.style(Style::default().fg(Color::Gray));
|
||||
|
||||
f.render_widget(help, chunks[1]);
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
let data = FullDemoData::new();
|
||||
let mut editor = EnhancedFormEditor::new(data);
|
||||
editor.set_mode(AppMode::ReadOnly); // Start in normal mode
|
||||
|
||||
// Set initial cursor style
|
||||
update_cursor_for_mode(editor.mode())?;
|
||||
|
||||
let res = run_app(&mut terminal, editor);
|
||||
|
||||
// Reset cursor style on exit
|
||||
execute!(io::stdout(), SetCursorStyle::DefaultUserShape)?;
|
||||
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -218,7 +218,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌫ Deleted character backward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn delete_forward(&mut self) -> anyhow::Result<()> {
|
||||
@@ -227,7 +227,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌦ Deleted character forward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === SUGGESTIONS CONTROL WRAPPERS ===
|
||||
@@ -259,7 +259,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
if result.is_ok() {
|
||||
self.has_unsaved_changes = true;
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === MANUAL CURSOR OVERRIDE DEMONSTRATION ===
|
||||
@@ -562,7 +562,7 @@ impl ProductionSuggestionsProvider {
|
||||
query.is_empty() || item.to_lowercase().starts_with(&query_lower)
|
||||
})
|
||||
.map(|(item, description)| SuggestionItem {
|
||||
display_text: format!("{} - {}", item, description),
|
||||
display_text: format!("{item} - {description}"),
|
||||
value_to_store: item.to_string(),
|
||||
})
|
||||
.collect()
|
||||
@@ -625,7 +625,7 @@ async fn handle_key_press(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {}", e));
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -639,7 +639,7 @@ async fn handle_key_press(
|
||||
(_, KeyCode::Enter, _) => {
|
||||
if editor.is_suggestions_active() {
|
||||
if let Some(applied) = editor.apply_suggestion() {
|
||||
editor.set_debug_message(format!("✅ Selected: {}", applied));
|
||||
editor.set_debug_message(format!("✅ Selected: {applied}"));
|
||||
} else {
|
||||
editor.set_debug_message("❌ No suggestion selected".to_string());
|
||||
}
|
||||
@@ -647,7 +647,7 @@ async fn handle_key_press(
|
||||
editor.next_field();
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!("Enter: moved to {} field", field_name));
|
||||
editor.set_debug_message(format!("Enter: moved to {field_name} field"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,7 +726,7 @@ async fn handle_key_press(
|
||||
editor.move_down();
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!("↓ moved to {} field", field_name));
|
||||
editor.set_debug_message(format!("↓ moved to {field_name} field"));
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('k'), _)
|
||||
@@ -734,7 +734,7 @@ async fn handle_key_press(
|
||||
editor.move_up();
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!("↑ moved to {} field", field_name));
|
||||
editor.set_debug_message(format!("↑ moved to {field_name} field"));
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
|
||||
@@ -829,7 +829,7 @@ async fn handle_key_press(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {}", e));
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -850,7 +850,7 @@ async fn handle_key_press(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {}", e));
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -883,7 +883,7 @@ async fn handle_key_press(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {}", e));
|
||||
editor.set_debug_message(format!("❌ Suggestion error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -912,8 +912,7 @@ async fn handle_key_press(
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let current_field = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!(
|
||||
"{} field - Try: i=insert, Tab=suggestions, j/k=move. Key: {:?}",
|
||||
current_field, key
|
||||
"{current_field} field - Try: i=insert, Tab=suggestions, j/k=move. Key: {key:?}"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -939,7 +938,7 @@ async fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -962,7 +961,7 @@ fn ui(f: &mut Frame, editor: &AutoCursorFormEditor<ApplicationData>) {
|
||||
f,
|
||||
chunks[0],
|
||||
input_rect,
|
||||
&canvas::canvas::theme::DefaultCanvasTheme::default(),
|
||||
&canvas::canvas::theme::DefaultCanvasTheme,
|
||||
editor.inner(),
|
||||
);
|
||||
}
|
||||
@@ -1110,7 +1109,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🚀 Ready to integrate this architecture into your production app!");
|
||||
|
||||
@@ -210,7 +210,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌫ Deleted character backward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn delete_forward(&mut self) -> anyhow::Result<()> {
|
||||
@@ -219,7 +219,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌦ Deleted character forward".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === SUGGESTIONS CONTROL WRAPPERS ===
|
||||
@@ -251,7 +251,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
if result.is_ok() {
|
||||
self.has_unsaved_changes = true;
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === PRODUCTION-READY NON-BLOCKING SUGGESTIONS ===
|
||||
@@ -275,7 +275,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
if applied {
|
||||
self.editor.update_inline_completion();
|
||||
if self.editor.suggestions().is_empty() {
|
||||
self.set_debug_message(format!("🔍 No matches for '{}'", query));
|
||||
self.set_debug_message(format!("🔍 No matches for '{query}'"));
|
||||
} else {
|
||||
self.set_debug_message(format!("✨ {} matches for '{}'", self.editor.suggestions().len(), query));
|
||||
}
|
||||
@@ -283,7 +283,7 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
||||
// If not applied, results were stale (user kept typing)
|
||||
}
|
||||
Err(e) => {
|
||||
self.set_debug_message(format!("❌ Suggestion error: {}", e));
|
||||
self.set_debug_message(format!("❌ Suggestion error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -574,7 +574,7 @@ impl ProductionSuggestionsProvider {
|
||||
query.is_empty() || item.to_lowercase().starts_with(&query_lower)
|
||||
})
|
||||
.map(|(item, description)| SuggestionItem {
|
||||
display_text: format!("{} - {}", item, description),
|
||||
display_text: format!("{item} - {description}"),
|
||||
value_to_store: item.to_string(),
|
||||
})
|
||||
.collect()
|
||||
@@ -634,7 +634,7 @@ async fn handle_key_press(
|
||||
(_, KeyCode::Enter, _) => {
|
||||
if editor.is_suggestions_active() {
|
||||
if let Some(applied) = editor.apply_suggestion() {
|
||||
editor.set_debug_message(format!("✅ Selected: {}", applied));
|
||||
editor.set_debug_message(format!("✅ Selected: {applied}"));
|
||||
} else {
|
||||
editor.set_debug_message("❌ No suggestion selected".to_string());
|
||||
}
|
||||
@@ -642,7 +642,7 @@ async fn handle_key_press(
|
||||
editor.next_field();
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!("Enter: moved to {} field", field_name));
|
||||
editor.set_debug_message(format!("Enter: moved to {field_name} field"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,7 +722,7 @@ async fn handle_key_press(
|
||||
editor.move_down();
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!("↓ moved to {} field", field_name));
|
||||
editor.set_debug_message(format!("↓ moved to {field_name} field"));
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('k'), _)
|
||||
@@ -730,7 +730,7 @@ async fn handle_key_press(
|
||||
editor.move_up();
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!("↑ moved to {} field", field_name));
|
||||
editor.set_debug_message(format!("↑ moved to {field_name} field"));
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
|
||||
@@ -872,8 +872,7 @@ async fn handle_key_press(
|
||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||
let current_field = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||
editor.set_debug_message(format!(
|
||||
"{} field - Try: i=insert, Tab=suggestions, j/k=move. Key: {:?}",
|
||||
current_field, key
|
||||
"{current_field} field - Try: i=insert, Tab=suggestions, j/k=move. Key: {key:?}"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -899,7 +898,7 @@ async fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -922,7 +921,7 @@ fn ui(f: &mut Frame, editor: &AutoCursorFormEditor<ApplicationData>) {
|
||||
f,
|
||||
chunks[0],
|
||||
input_rect,
|
||||
&canvas::canvas::theme::DefaultCanvasTheme::default(),
|
||||
&canvas::canvas::theme::DefaultCanvasTheme,
|
||||
&editor.editor,
|
||||
);
|
||||
}
|
||||
@@ -1071,7 +1070,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🚀 Ready to integrate this architecture into your production app!");
|
||||
|
||||
@@ -35,7 +35,7 @@ use ratatui::{
|
||||
};
|
||||
|
||||
use canvas::{
|
||||
canvas::{modes::AppMode, CursorManager},
|
||||
canvas::CursorManager,
|
||||
textarea::{TextArea, TextAreaState},
|
||||
};
|
||||
|
||||
@@ -291,7 +291,7 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut editor: AutoCursorTextAre
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,7 +389,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🎯 Cursor automatically reset to default!");
|
||||
|
||||
@@ -339,13 +339,13 @@ fn handle_key_press(
|
||||
// Vim o/O commands
|
||||
(AppMode::ReadOnly, KeyCode::Char('o'), _) => {
|
||||
if let Err(e) = editor.open_line_below() {
|
||||
editor.set_debug_message(format!("Error opening line below: {}", e));
|
||||
editor.set_debug_message(format!("Error opening line below: {e}"));
|
||||
}
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('O'), _) => {
|
||||
if let Err(e) = editor.open_line_above() {
|
||||
editor.set_debug_message(format!("Error opening line above: {}", e));
|
||||
editor.set_debug_message(format!("Error opening line above: {e}"));
|
||||
}
|
||||
editor.clear_command_buffer();
|
||||
}
|
||||
@@ -482,8 +482,7 @@ fn handle_key_press(
|
||||
editor.set_debug_message("Invalid command sequence".to_string());
|
||||
} else {
|
||||
editor.set_debug_message(format!(
|
||||
"Unhandled: {:?} + {:?} in {:?} mode",
|
||||
key, modifiers, mode
|
||||
"Unhandled: {key:?} + {modifiers:?} in {mode:?} mode"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -507,7 +506,7 @@ fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,7 +644,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🎯 Cursor automatically reset to default!");
|
||||
|
||||
@@ -141,10 +141,10 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
self.debug_message = "✅ Current field is valid!".to_string();
|
||||
}
|
||||
ValidationResult::Warning { message } => {
|
||||
self.debug_message = format!("⚠️ Warning: {}", message);
|
||||
self.debug_message = format!("⚠️ Warning: {message}");
|
||||
}
|
||||
ValidationResult::Error { message } => {
|
||||
self.debug_message = format!("❌ Error: {}", message);
|
||||
self.debug_message = format!("❌ Error: {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +189,7 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
Err(e) => {
|
||||
self.field_switch_blocked = true;
|
||||
self.block_reason = Some(e.to_string());
|
||||
self.debug_message = format!("🚫 Field switch blocked: {}", e);
|
||||
self.debug_message = format!("🚫 Field switch blocked: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
Err(e) => {
|
||||
self.field_switch_blocked = true;
|
||||
self.block_reason = Some(e.to_string());
|
||||
self.debug_message = format!("🚫 Field switch blocked: {}", e);
|
||||
self.debug_message = format!("🚫 Field switch blocked: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,19 +289,19 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
if let Some(status) = limits.status_text(
|
||||
self.editor.data_provider().field_value(field_index)
|
||||
) {
|
||||
self.debug_message = format!("✏️ {}", status);
|
||||
self.debug_message = format!("✏️ {status}");
|
||||
}
|
||||
}
|
||||
}
|
||||
ValidationResult::Warning { message } => {
|
||||
self.debug_message = format!("⚠️ {}", message);
|
||||
self.debug_message = format!("⚠️ {message}");
|
||||
}
|
||||
ValidationResult::Error { message } => {
|
||||
self.debug_message = format!("❌ {}", message);
|
||||
self.debug_message = format!("❌ {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn get_current_field_limits(&self) -> Option<&CharacterLimits> {
|
||||
@@ -317,7 +317,7 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌫ Deleted character".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn delete_forward(&mut self) -> anyhow::Result<()> {
|
||||
@@ -326,7 +326,7 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
self.has_unsaved_changes = true;
|
||||
self.debug_message = "⌦ Deleted character".to_string();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === DELEGATE TO ORIGINAL EDITOR ===
|
||||
@@ -370,7 +370,7 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
Err(e) => {
|
||||
self.field_switch_blocked = true;
|
||||
self.block_reason = Some(e.to_string());
|
||||
self.debug_message = format!("🚫 Cannot move to next field: {}", e);
|
||||
self.debug_message = format!("🚫 Cannot move to next field: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,7 +385,7 @@ impl<D: DataProvider> ValidationFormEditor<D> {
|
||||
Err(e) => {
|
||||
self.field_switch_blocked = true;
|
||||
self.block_reason = Some(e.to_string());
|
||||
self.debug_message = format!("🚫 Cannot move to previous field: {}", e);
|
||||
self.debug_message = format!("🚫 Cannot move to previous field: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,7 +644,7 @@ fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -823,7 +823,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🔍 Validation demo completed!");
|
||||
|
||||
@@ -93,14 +93,14 @@ impl<D: DataProvider> AdvancedPatternFormEditor<D> {
|
||||
fn move_up(&mut self) {
|
||||
match self.editor.move_up() {
|
||||
Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Field switch blocked: {}", e); }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Field switch blocked: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
fn move_down(&mut self) {
|
||||
match self.editor.move_down() {
|
||||
Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Field switch blocked: {}", e); }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Field switch blocked: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,23 +132,23 @@ impl<D: DataProvider> AdvancedPatternFormEditor<D> {
|
||||
let validation_result = self.editor.validate_current_field();
|
||||
match validation_result {
|
||||
ValidationResult::Valid => { self.debug_message = "✅ Character accepted".to_string(); }
|
||||
ValidationResult::Warning { message } => { self.debug_message = format!("⚠️ Warning: {}", message); }
|
||||
ValidationResult::Error { message } => { self.debug_message = format!("❌ Pattern violation: {}", message); }
|
||||
ValidationResult::Warning { message } => { self.debug_message = format!("⚠️ Warning: {message}"); }
|
||||
ValidationResult::Error { message } => { self.debug_message = format!("❌ Pattern violation: {message}"); }
|
||||
}
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn delete_backward(&mut self) -> anyhow::Result<()> {
|
||||
let result = self.editor.delete_backward();
|
||||
if result.is_ok() { self.debug_message = "⌫ Character deleted".to_string(); }
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn delete_forward(&mut self) -> anyhow::Result<()> {
|
||||
let result = self.editor.delete_forward();
|
||||
if result.is_ok() { self.debug_message = "⌦ Character deleted".to_string(); }
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// Delegate methods
|
||||
@@ -166,14 +166,14 @@ impl<D: DataProvider> AdvancedPatternFormEditor<D> {
|
||||
fn next_field(&mut self) {
|
||||
match self.editor.next_field() {
|
||||
Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Cannot move to next field: {}", e); }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Cannot move to next field: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
fn prev_field(&mut self) {
|
||||
match self.editor.prev_field() {
|
||||
Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Cannot move to previous field: {}", e); }
|
||||
Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Cannot move to previous field: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -654,7 +654,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🚀 Advanced pattern validation demo completed!");
|
||||
|
||||
@@ -54,7 +54,6 @@ use canvas::{
|
||||
},
|
||||
DataProvider, FormEditor,
|
||||
ValidationConfig, ValidationConfigBuilder, DisplayMask,
|
||||
validation::mask::MaskDisplayMode,
|
||||
};
|
||||
|
||||
// Enhanced FormEditor wrapper for mask demonstration
|
||||
@@ -144,14 +143,14 @@ impl<D: DataProvider> MaskDemoFormEditor<D> {
|
||||
fn move_up(&mut self) {
|
||||
match self.editor.move_up() {
|
||||
Ok(()) => { self.update_field_info(); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Field switch blocked: {}", e); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Field switch blocked: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
fn move_down(&mut self) {
|
||||
match self.editor.move_down() {
|
||||
Ok(()) => { self.update_field_info(); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Field switch blocked: {}", e); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Field switch blocked: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,16 +169,16 @@ impl<D: DataProvider> MaskDemoFormEditor<D> {
|
||||
let raw_pos = self.editor.cursor_position();
|
||||
let display_pos = self.editor.display_cursor_position();
|
||||
if raw_pos != display_pos {
|
||||
self.debug_message = format!("📍 Cursor: Raw pos {} → Display pos {} (mask active)", raw_pos, display_pos);
|
||||
self.debug_message = format!("📍 Cursor: Raw pos {raw_pos} → Display pos {display_pos} (mask active)");
|
||||
} else {
|
||||
self.debug_message = format!("📍 Cursor at position {} (no mask offset)", raw_pos);
|
||||
self.debug_message = format!("📍 Cursor at position {raw_pos} (no mask offset)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_field_info(&mut self) {
|
||||
let field_name = self.editor.data_provider().field_name(self.editor.current_field());
|
||||
self.debug_message = format!("📝 Switched to: {}", field_name);
|
||||
self.debug_message = format!("📝 Switched to: {field_name}");
|
||||
}
|
||||
|
||||
// === MODE TRANSITIONS ===
|
||||
@@ -206,12 +205,12 @@ impl<D: DataProvider> MaskDemoFormEditor<D> {
|
||||
if result.is_ok() {
|
||||
let (raw, display, _) = self.get_current_field_info();
|
||||
if raw != display {
|
||||
self.debug_message = format!("✏️ Added '{}': Raw='{}' Display='{}'", ch, raw, display);
|
||||
self.debug_message = format!("✏️ Added '{ch}': Raw='{raw}' Display='{display}'");
|
||||
} else {
|
||||
self.debug_message = format!("✏️ Added '{}': '{}'", ch, raw);
|
||||
self.debug_message = format!("✏️ Added '{ch}': '{raw}'");
|
||||
}
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === DELETE OPERATIONS ===
|
||||
@@ -221,7 +220,7 @@ impl<D: DataProvider> MaskDemoFormEditor<D> {
|
||||
self.debug_message = "⌫ Character deleted".to_string();
|
||||
self.update_cursor_info();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
fn delete_forward(&mut self) -> anyhow::Result<()> {
|
||||
@@ -230,7 +229,7 @@ impl<D: DataProvider> MaskDemoFormEditor<D> {
|
||||
self.debug_message = "⌦ Character deleted".to_string();
|
||||
self.update_cursor_info();
|
||||
}
|
||||
Ok(result?)
|
||||
result
|
||||
}
|
||||
|
||||
// === DELEGATE TO ORIGINAL EDITOR ===
|
||||
@@ -251,14 +250,14 @@ impl<D: DataProvider> MaskDemoFormEditor<D> {
|
||||
fn next_field(&mut self) {
|
||||
match self.editor.next_field() {
|
||||
Ok(()) => { self.update_field_info(); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Cannot move to next field: {}", e); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Cannot move to next field: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
fn prev_field(&mut self) {
|
||||
match self.editor.prev_field() {
|
||||
Ok(()) => { self.update_field_info(); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Cannot move to previous field: {}", e); }
|
||||
Err(e) => { self.debug_message = format!("🚫 Cannot move to previous field: {e}"); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +286,7 @@ impl<D: DataProvider> MaskDemoFormEditor<D> {
|
||||
}
|
||||
}
|
||||
|
||||
format!("🎭 {} MASKS", mask_count)
|
||||
format!("🎭 {mask_count} MASKS")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,7 +548,7 @@ fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.set_debug_message(format!("Error: {}", e));
|
||||
editor.set_debug_message(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -726,7 +725,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
CursorManager::reset()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🎭 Display mask demo completed!");
|
||||
|
||||
@@ -97,7 +97,7 @@ impl CustomFormatter for PhoneFormatter {
|
||||
let len = raw.chars().count();
|
||||
match len {
|
||||
0 => FormattingResult::success(""),
|
||||
1..=3 => FormattingResult::success(format!("({})", raw)),
|
||||
1..=3 => FormattingResult::success(format!("({raw})")),
|
||||
4..=6 => FormattingResult::success(format!("({}) {}", &raw[..3], &raw[3..])),
|
||||
7..=10 => FormattingResult::success(format!("({}) {}-{}", &raw[..3], &raw[3..6], &raw[6..])),
|
||||
10 => {
|
||||
@@ -135,7 +135,7 @@ impl CustomFormatter for CreditCardFormatter {
|
||||
|
||||
let len = raw.chars().count();
|
||||
match len {
|
||||
0..=15 => FormattingResult::warning(formatted, format!("Card incomplete ({}/16 digits)", len)),
|
||||
0..=15 => FormattingResult::warning(formatted, format!("Card incomplete ({len}/16 digits)")),
|
||||
16 => FormattingResult::success(formatted),
|
||||
_ => FormattingResult::warning(formatted, "Card too long (extra digits shown)"),
|
||||
}
|
||||
@@ -177,16 +177,16 @@ impl CustomFormatter for DateFormatter {
|
||||
|
||||
if m == 0 || m > 12 {
|
||||
FormattingResult::warning(
|
||||
format!("{}/{}/{}", month, day, year),
|
||||
format!("{month}/{day}/{year}"),
|
||||
"Invalid month (01-12)"
|
||||
)
|
||||
} else if d == 0 || d > 31 {
|
||||
FormattingResult::warning(
|
||||
format!("{}/{}/{}", month, day, year),
|
||||
format!("{month}/{day}/{year}"),
|
||||
"Invalid day (01-31)"
|
||||
)
|
||||
} else {
|
||||
FormattingResult::success(format!("{}/{}/{}", month, day, year))
|
||||
FormattingResult::success(format!("{month}/{day}/{year}"))
|
||||
}
|
||||
},
|
||||
_ => FormattingResult::error("Date too long (MMDDYYYY format)"),
|
||||
@@ -384,7 +384,7 @@ impl<D: DataProvider> EnhancedDemoEditor<D> {
|
||||
|
||||
let warning = if self.validation_enabled && self.has_formatter() {
|
||||
// Check if there are any formatting warnings
|
||||
if raw.len() > 0 {
|
||||
if !raw.is_empty() {
|
||||
match self.editor.current_field() {
|
||||
0 if raw.len() < 5 => Some(format!("PSC incomplete: {}/5", raw.len())),
|
||||
1 if raw.len() < 10 => Some(format!("Phone incomplete: {}/10", raw.len())),
|
||||
@@ -408,7 +408,7 @@ impl<D: DataProvider> EnhancedDemoEditor<D> {
|
||||
self.editor.enter_edit_mode();
|
||||
let field_type = self.current_field_type();
|
||||
let rules = self.get_input_rules();
|
||||
self.debug_message = format!("✏️ INSERT MODE - Cursor: Steady Bar | - {} - {}", field_type, rules);
|
||||
self.debug_message = format!("✏️ INSERT MODE - Cursor: Steady Bar | - {field_type} - {rules}");
|
||||
}
|
||||
|
||||
fn exit_edit_mode(&mut self) {
|
||||
@@ -429,9 +429,9 @@ impl<D: DataProvider> EnhancedDemoEditor<D> {
|
||||
if result.is_ok() {
|
||||
let (raw, display, _, _) = self.get_current_field_analysis();
|
||||
if raw != display && self.validation_enabled {
|
||||
self.debug_message = format!("✏️ '{}' added - Real-time formatting active", ch);
|
||||
self.debug_message = format!("✏️ '{ch}' added - Real-time formatting active");
|
||||
} else {
|
||||
self.debug_message = format!("✏️ '{}' added", ch);
|
||||
self.debug_message = format!("✏️ '{ch}' added");
|
||||
}
|
||||
}
|
||||
result
|
||||
@@ -459,7 +459,7 @@ impl<D: DataProvider> EnhancedDemoEditor<D> {
|
||||
display.chars().nth(display_pos).unwrap_or('∅')
|
||||
);
|
||||
} else {
|
||||
self.debug_message = format!("📍 Cursor at position {} (no mapping needed)", raw_pos);
|
||||
self.debug_message = format!("📍 Cursor at position {raw_pos} (no mapping needed)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ fn handle_key_press(
|
||||
// Field analysis
|
||||
(AppMode::ReadOnly, KeyCode::Char('?'), _) => {
|
||||
let (raw, display, status, warning) = editor.get_current_field_analysis();
|
||||
let warning_text = warning.map(|w| format!(" ⚠️ {}", w)).unwrap_or_default();
|
||||
let warning_text = warning.map(|w| format!(" ⚠️ {w}")).unwrap_or_default();
|
||||
editor.debug_message = format!(
|
||||
"🔍 Field {}: {} | Raw: '{}' | Display: '{}'{}",
|
||||
editor.current_field() + 1, status, raw, display, warning_text
|
||||
@@ -558,7 +558,7 @@ fn run_app<B: Backend>(
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
editor.debug_message = format!("❌ Error: {}", e);
|
||||
editor.debug_message = format!("❌ Error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,11 +627,11 @@ fn render_enhanced_status(
|
||||
];
|
||||
|
||||
if editor.show_raw_data || editor.mode() == AppMode::Edit {
|
||||
analysis_lines.push(format!("💾 Raw Data: '{}'", raw));
|
||||
analysis_lines.push(format!("✨ Display: '{}'", display));
|
||||
analysis_lines.push(format!("💾 Raw Data: '{raw}'"));
|
||||
analysis_lines.push(format!("✨ Display: '{display}'"));
|
||||
} else {
|
||||
analysis_lines.push(format!("✨ User Sees: '{}'", display));
|
||||
analysis_lines.push(format!("💾 Stored As: '{}'", raw));
|
||||
analysis_lines.push(format!("✨ User Sees: '{display}'"));
|
||||
analysis_lines.push(format!("💾 Stored As: '{raw}'"));
|
||||
}
|
||||
|
||||
if editor.show_cursor_details {
|
||||
@@ -643,7 +643,7 @@ fn render_enhanced_status(
|
||||
}
|
||||
|
||||
if let Some(ref warn) = warning {
|
||||
analysis_lines.push(format!("⚠️ Warning: {}", warn));
|
||||
analysis_lines.push(format!("⚠️ Warning: {warn}"));
|
||||
}
|
||||
|
||||
let analysis_color = if warning.is_some() {
|
||||
@@ -742,7 +742,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
CursorManager::reset()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🧩 Enhanced custom formatter demo completed!");
|
||||
|
||||
@@ -203,7 +203,7 @@ impl ValidationServices {
|
||||
|
||||
/// PSC validation: simulates postal service API lookup
|
||||
fn validate_psc(&mut self, psc: &str) -> ExternalValidationState {
|
||||
let cache_key = format!("psc:{}", psc);
|
||||
let cache_key = format!("psc:{psc}");
|
||||
if let Some(cached) = self.cache.get(&cache_key) {
|
||||
return cached.clone();
|
||||
}
|
||||
@@ -244,7 +244,7 @@ impl ValidationServices {
|
||||
"20" | "21" => "Brno region",
|
||||
_ => "Valid postal region"
|
||||
};
|
||||
ExternalValidationState::Valid(Some(format!("{} - verified", region)))
|
||||
ExternalValidationState::Valid(Some(format!("{region} - verified")))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -254,7 +254,7 @@ impl ValidationServices {
|
||||
|
||||
/// Email validation: simulates domain checking
|
||||
fn validate_email(&mut self, email: &str) -> ExternalValidationState {
|
||||
let cache_key = format!("email:{}", email);
|
||||
let cache_key = format!("email:{email}");
|
||||
if let Some(cached) = self.cache.get(&cache_key) {
|
||||
return cached.clone();
|
||||
}
|
||||
@@ -315,7 +315,7 @@ impl ValidationServices {
|
||||
|
||||
/// Username validation: simulates availability checking
|
||||
fn validate_username(&mut self, username: &str) -> ExternalValidationState {
|
||||
let cache_key = format!("username:{}", username);
|
||||
let cache_key = format!("username:{username}");
|
||||
if let Some(cached) = self.cache.get(&cache_key) {
|
||||
return cached.clone();
|
||||
}
|
||||
@@ -371,7 +371,7 @@ impl ValidationServices {
|
||||
|
||||
/// API Key validation: simulates authentication service
|
||||
fn validate_api_key(&mut self, key: &str) -> ExternalValidationState {
|
||||
let cache_key = format!("apikey:{}", key);
|
||||
let cache_key = format!("apikey:{key}");
|
||||
if let Some(cached) = self.cache.get(&cache_key) {
|
||||
return cached.clone();
|
||||
}
|
||||
@@ -429,7 +429,7 @@ impl ValidationServices {
|
||||
|
||||
/// Credit Card validation: simulates bank verification
|
||||
fn validate_credit_card(&mut self, card: &str) -> ExternalValidationState {
|
||||
let cache_key = format!("card:{}", card);
|
||||
let cache_key = format!("card:{card}");
|
||||
if let Some(cached) = self.cache.get(&cache_key) {
|
||||
return cached.clone();
|
||||
}
|
||||
@@ -724,8 +724,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
||||
let duration_ms = result.duration().as_millis();
|
||||
let cached_text = if result.cached { " (cached)" } else { "" };
|
||||
self.debug_message = format!(
|
||||
"🔍 {} validation completed in {}ms{} (manual)",
|
||||
validation_type, duration_ms, cached_text
|
||||
"🔍 {validation_type} validation completed in {duration_ms}ms{cached_text} (manual)"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -812,7 +811,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
||||
0
|
||||
};
|
||||
|
||||
format!("Total: {} validations, Avg: {}ms", total_validations, avg_time_ms)
|
||||
format!("Total: {total_validations} validations, Avg: {avg_time_ms}ms")
|
||||
}
|
||||
|
||||
fn get_field_validation_state(&self, field_index: usize) -> ExternalValidationState {
|
||||
@@ -1032,8 +1031,8 @@ fn render_validation_panel(
|
||||
};
|
||||
|
||||
let field_line = Line::from(vec![
|
||||
Span::styled(format!("{}{}: ", indicator, field_name), Style::default().fg(Color::White)),
|
||||
Span::raw(format!("'{}' → ", value_display)),
|
||||
Span::styled(format!("{indicator}{field_name}: "), Style::default().fg(Color::White)),
|
||||
Span::raw(format!("'{value_display}' → ")),
|
||||
Span::styled(state_text.to_string(), Style::default().fg(color)),
|
||||
]);
|
||||
|
||||
@@ -1077,8 +1076,7 @@ fn render_validation_panel(
|
||||
};
|
||||
|
||||
ListItem::new(format!(
|
||||
"{}: '{}' → {} ({}ms{})",
|
||||
field_name, short_value, state_summary, duration_ms, cached_text
|
||||
"{field_name}: '{short_value}' → {state_summary} ({duration_ms}ms{cached_text})"
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
@@ -1162,7 +1160,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
CursorManager::reset()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err);
|
||||
println!("{err:?}");
|
||||
}
|
||||
|
||||
println!("🧪 Enhanced fully automatic external validation demo completed!");
|
||||
|
||||
Reference in New Issue
Block a user