cleared codebase

This commit is contained in:
Priec
2025-08-13 01:22:50 +02:00
parent 2b16a80ef8
commit 3227d341ed
5 changed files with 156 additions and 118 deletions

View File

@@ -88,42 +88,27 @@ pub fn render_canvas_with_highlight<T: CanvasTheme, D: DataProvider>(
highlight_state,
editor.display_cursor_position(), // Use display cursor position for masks
false, // TODO: track unsaved changes in editor
|i| {
// Get display value for field i using editor logic (Feature 4 + masks)
#[cfg(feature = "validation")]
{
editor.display_text_for_field(i)
}
#[cfg(not(feature = "validation"))]
{
data_provider.field_value(i).to_string()
}
// Closures for getting display values and overrides
#[cfg(feature = "validation")]
|field_idx| editor.display_text_for_field(field_idx),
#[cfg(not(feature = "validation"))]
|field_idx| data_provider.field_value(field_idx).to_string(),
// Closure for checking display overrides
#[cfg(feature = "validation")]
|field_idx| {
editor.ui_state().validation_state().get_field_config(field_idx)
.map(|cfg| {
let has_formatter = cfg.custom_formatter.is_some();
let has_mask = cfg.display_mask.is_some();
has_formatter || has_mask
})
.unwrap_or(false)
},
|i| {
// Check if field has display override (custom formatter or mask)
#[cfg(feature = "validation")]
{
editor.ui_state().validation_state().get_field_config(i)
.map(|cfg| {
// Formatter takes precedence; if present, it's a display override
#[allow(unused_mut)]
let mut has_override = false;
#[cfg(feature = "validation")]
{
has_override = cfg.custom_formatter.is_some();
}
has_override || cfg.display_mask.is_some()
})
.unwrap_or(false)
}
#[cfg(not(feature = "validation"))]
{
false
}
},
// NEW: provide completion for the active field
|i| {
if i == current_field_idx {
#[cfg(not(feature = "validation"))]
|_field_idx| false,
// Closure for providing completion
|field_idx| {
if field_idx == current_field_idx {
active_completion.clone()
} else {
None
@@ -269,7 +254,8 @@ where
{
let mut active_field_input_rect = None;
for (i, input) in inputs.iter().enumerate() {
// FIX: Iterate over indices only since we never use the input values directly
for i in 0..inputs.len() {
let is_active = i == *current_field_idx;
let typed_text = get_display_value(i);
@@ -353,7 +339,7 @@ fn apply_characterwise_highlighting<'a, T: CanvasTheme>(
current_cursor_pos: usize,
anchor: &(usize, usize),
theme: &T,
is_active: bool,
_is_active: bool,
) -> Line<'a> {
let (anchor_field, anchor_char) = *anchor;
let start_field = min(anchor_field, *current_field_idx);
@@ -456,7 +442,7 @@ fn apply_linewise_highlighting<'a, T: CanvasTheme>(
current_field_idx: &usize,
anchor_line: &usize,
theme: &T,
is_active: bool,
_is_active: bool,
) -> Line<'a> {
let start_field = min(*anchor_line, *current_field_idx);
let end_field = max(*anchor_line, *current_field_idx);
@@ -487,7 +473,7 @@ fn set_cursor_position(
field_rect: Rect,
text: &str,
current_cursor_pos: usize,
has_display_override: bool,
_has_display_override: bool,
) {
// Sum display widths of the first current_cursor_pos characters
let mut cols: u16 = 0;

View File

@@ -25,7 +25,7 @@ pub trait ComputedProvider {
/// Get list of field dependencies for optimization.
/// If field A depends on fields [1, 3], only recompute A when fields 1 or 3 change.
/// Default: depend on all fields (always recompute) with a reasonable upper bound.
fn field_dependencies(&self, field_index: usize) -> Vec<usize> {
fn field_dependencies(&self, _field_index: usize) -> Vec<usize> {
(0..100).collect()
}
}

View File

@@ -19,33 +19,33 @@ pub trait DataProvider {
fn set_field_value(&mut self, index: usize, value: String);
/// Check if field supports suggestions (optional)
fn supports_suggestions(&self, field_index: usize) -> bool {
fn supports_suggestions(&self, _field_index: usize) -> bool {
false
}
/// Get display value (for password masking, etc.) - optional
fn display_value(&self, index: usize) -> Option<&str> {
fn display_value(&self, _index: usize) -> Option<&str> {
None // Default: use actual value
}
/// Get validation configuration for a field (optional)
/// Only available when the 'validation' feature is enabled
#[cfg(feature = "validation")]
fn validation_config(&self, field_index: usize) -> Option<crate::validation::ValidationConfig> {
fn validation_config(&self, _field_index: usize) -> Option<crate::validation::ValidationConfig> {
None
}
/// Check if field is computed (display-only, skip in navigation)
/// Default: not computed
#[cfg(feature = "computed")]
fn is_computed_field(&self, field_index: usize) -> bool {
fn is_computed_field(&self, _field_index: usize) -> bool {
false
}
/// Get computed field value if this is a computed field.
/// Returns None for regular fields. Default: not computed.
#[cfg(feature = "computed")]
fn computed_field_value(&self, field_index: usize) -> Option<String> {
fn computed_field_value(&self, _field_index: usize) -> Option<String> {
None
}
}

View File

@@ -4,13 +4,9 @@
#[cfg(feature = "cursor-style")]
use crate::canvas::CursorManager;
use anyhow::Result;
use crate::canvas::state::EditorState;
use crate::{DataProvider, SuggestionItem};
#[cfg(feature = "suggestions")]
use crate::SuggestionsProvider;
use crate::canvas::modes::AppMode;
use crate::canvas::state::SelectionState;
@@ -49,8 +45,9 @@ impl<D: DataProvider> FormEditor<D> {
fn byte_to_char_index(s: &str, byte_idx: usize) -> usize {
s[..byte_idx].chars().count()
}
pub fn new(data_provider: D) -> Self {
let mut editor = Self {
let editor = Self {
ui_state: EditorState::new(),
data_provider,
suggestions: Vec::new(),
@@ -61,10 +58,14 @@ impl<D: DataProvider> FormEditor<D> {
// Initialize validation configurations if validation feature is enabled
#[cfg(feature = "validation")]
{
let mut editor = editor;
editor.initialize_validation();
editor
}
#[cfg(not(feature = "validation"))]
{
editor
}
editor
}
/// Get current field text (convenience method)
@@ -160,7 +161,7 @@ impl<D: DataProvider> FormEditor<D> {
if matches!(self.ui_state.current_mode, AppMode::Edit) {
return raw.to_string();
}
if let Some((formatted, mapper, warning)) = cfg.run_custom_formatter(raw) {
if let Some((formatted, _mapper, _warning)) = cfg.run_custom_formatter(raw) {
return formatted;
}
}
@@ -179,11 +180,6 @@ impl<D: DataProvider> FormEditor<D> {
&self.ui_state
}
/// Mutable access to UI state for internal crate use only.
pub(crate) fn ui_state_mut(&mut self) -> &mut EditorState {
&mut self.ui_state
}
/// Open the suggestions UI for `field_index` (UI-only; does not fetch).
pub fn open_suggestions(&mut self, field_index: usize) {
self.ui_state.open_suggestions(field_index);
@@ -257,7 +253,7 @@ impl<D: DataProvider> FormEditor<D> {
return raw.to_string();
}
// Not editing -> formatted
if let Some((formatted, mapper, warning)) = cfg.run_custom_formatter(raw) {
if let Some((formatted, _mapper, _warning)) = cfg.run_custom_formatter(raw) {
return formatted;
}
}
@@ -305,7 +301,6 @@ impl<D: DataProvider> FormEditor<D> {
// ===================================================================
/// Centralized field transition logic
#[cfg_attr(not(feature = "validation"), allow(unused_variables))]
pub fn transition_to_field(&mut self, new_field: usize) -> Result<()> {
let field_count = self.data_provider.field_count();
if field_count == 0 {
@@ -314,8 +309,11 @@ impl<D: DataProvider> FormEditor<D> {
let prev_field = self.ui_state.current_field;
// 1. Bounds check
// FIX 2: Only mut when computed feature actually modifies it
#[cfg(feature = "computed")]
let mut target_field = new_field.min(field_count - 1);
#[cfg(not(feature = "computed"))]
let target_field = new_field.min(field_count - 1);
// 2. Computed field skipping
#[cfg(feature = "computed")]
@@ -432,8 +430,20 @@ impl<D: DataProvider> FormEditor<D> {
return Ok(()); // Ignore in non-edit modes
}
// Variables are only declared when the features that use them are enabled
#[cfg(feature = "validation")]
let field_index = self.ui_state.current_field;
#[cfg(feature = "validation")]
let raw_cursor_pos = self.ui_state.cursor_pos;
#[cfg(feature = "validation")]
let current_raw_text = self.data_provider.field_value(field_index);
// When validation is disabled, we declare these variables differently
#[cfg(not(feature = "validation"))]
let field_index = self.ui_state.current_field;
#[cfg(not(feature = "validation"))]
let raw_cursor_pos = self.ui_state.cursor_pos;
#[cfg(not(feature = "validation"))]
let current_raw_text = self.data_provider.field_value(field_index);
// Mask gate: reject input that doesn't fit the mask at current position
@@ -566,7 +576,12 @@ impl<D: DataProvider> FormEditor<D> {
/// Restore left and right movement within the current field
/// Move cursor left within current field
pub fn move_left(&mut self) -> Result<()> {
// FIX 3: Only mut when validation feature modifies it
#[cfg(feature = "validation")]
let mut moved = false;
#[cfg(not(feature = "validation"))]
let moved = false;
// Try mask-aware movement if validation/mask config exists
#[cfg(feature = "validation")]
{
@@ -600,11 +615,16 @@ impl<D: DataProvider> FormEditor<D> {
/// Move cursor right within current field
pub fn move_right(&mut self) -> Result<()> {
// FIX 4: Only mut when validation feature modifies it
#[cfg(feature = "validation")]
let mut moved = false;
let field_index = self.ui_state.current_field;
#[cfg(not(feature = "validation"))]
let moved = false;
// Try mask-aware movement if mask is configured for this field
#[cfg(feature = "validation")]
{
let field_index = self.ui_state.current_field;
if let Some(cfg) = self.ui_state.validation.get_field_config(field_index) {
if let Some(mask) = &cfg.display_mask {
let display_pos = mask.raw_pos_to_display_pos(self.ui_state.cursor_pos);
@@ -771,7 +791,7 @@ impl<D: DataProvider> FormEditor<D> {
pub fn current_formatter_warning(&self) -> Option<String> {
let idx = self.ui_state.current_field;
if let Some(cfg) = self.ui_state.validation.get_field_config(idx) {
if let Some((fmt, mapper, warn)) = cfg.run_custom_formatter(self.current_text()) {
if let Some((_fmt, _mapper, warn)) = cfg.run_custom_formatter(self.current_text()) {
return warn;
}
}
@@ -814,6 +834,7 @@ impl<D: DataProvider> FormEditor<D> {
///
/// The caller is responsible for fetching suggestions and calling
/// `apply_suggestions_result()` when ready.
#[cfg(feature = "suggestions")]
pub fn start_suggestions(&mut self, field_index: usize) -> Option<String> {
if !self.data_provider.supports_suggestions(field_index) {
return None;
@@ -837,12 +858,18 @@ impl<D: DataProvider> FormEditor<D> {
Some(query)
}
#[cfg(not(feature = "suggestions"))]
pub fn start_suggestions(&mut self, _field_index: usize) -> Option<String> {
None
}
/// Apply fetched suggestions results
///
/// This will ignore stale results if the field or query has changed since
/// `start_suggestions()` was called.
///
/// Returns `true` if results were applied, `false` if they were stale/ignored.
#[cfg(feature = "suggestions")]
pub fn apply_suggestions_result(
&mut self,
field_index: usize,
@@ -874,9 +901,20 @@ impl<D: DataProvider> FormEditor<D> {
true
}
#[cfg(not(feature = "suggestions"))]
pub fn apply_suggestions_result(
&mut self,
_field_index: usize,
_query: &str,
_results: Vec<SuggestionItem>,
) -> bool {
false
}
/// Check if there's an active suggestions query waiting for results
///
/// Returns (field_index, query) if suggestions are loading, None otherwise.
#[cfg(feature = "suggestions")]
pub fn pending_suggestions_query(&self) -> Option<(usize, String)> {
if self.ui_state.suggestions.is_loading {
if let (Some(field), Some(query)) = (
@@ -889,6 +927,11 @@ impl<D: DataProvider> FormEditor<D> {
None
}
#[cfg(not(feature = "suggestions"))]
pub fn pending_suggestions_query(&self) -> Option<(usize, String)> {
None
}
/// Cancel any pending suggestions (useful for cleanup)
pub fn cancel_suggestions(&mut self) {
self.close_suggestions();
@@ -932,7 +975,7 @@ impl<D: DataProvider> FormEditor<D> {
// Validate the new content if validation is enabled
#[cfg(feature = "validation")]
{
let validation_result = self.ui_state.validation.validate_field_content(
let _validation_result = self.ui_state.validation.validate_field_content(
field_index,
&suggestion.value_to_store,
);
@@ -1197,8 +1240,12 @@ impl<D: DataProvider> FormEditor<D> {
current_text.replace_range(start..end, "");
self.data_provider.set_field_value(field_index, current_text.clone());
// Always run reposition logic
// FIX 5: Only mut when validation feature might modify it
#[cfg(feature = "validation")]
let mut target_cursor = new_cursor;
#[cfg(not(feature = "validation"))]
let target_cursor = new_cursor;
#[cfg(feature = "validation")]
{
if let Some(cfg) = self.ui_state.validation.get_field_config(field_index) {
@@ -1240,7 +1287,12 @@ impl<D: DataProvider> FormEditor<D> {
current_text.replace_range(start..end, "");
self.data_provider.set_field_value(field_index, current_text.clone());
// FIX 6: Only mut when validation feature might modify it
#[cfg(feature = "validation")]
let mut target_cursor = self.ui_state.cursor_pos;
#[cfg(not(feature = "validation"))]
let target_cursor = self.ui_state.cursor_pos;
#[cfg(feature = "validation")]
{
if let Some(cfg) = self.ui_state.validation.get_field_config(field_index) {
@@ -1349,22 +1401,6 @@ impl<D: DataProvider> FormEditor<D> {
// HELPER METHODS
// ===================================================================
/// Clamp cursor position to valid bounds for current field and mode
fn clamp_cursor_to_current_field(&mut self) {
let current_text = self.current_text();
let is_edit_mode = self.ui_state.current_mode == AppMode::Edit;
use crate::canvas::actions::movement::line::safe_cursor_position;
let safe_pos = safe_cursor_position(
current_text,
self.ui_state.ideal_cursor_column,
is_edit_mode
);
self.ui_state.cursor_pos = safe_pos;
}
/// Set the value of the current field
pub fn set_current_field_value(&mut self, value: String) {
let field_index = self.ui_state.current_field;
@@ -1376,7 +1412,7 @@ impl<D: DataProvider> FormEditor<D> {
// Validate the new content if validation is enabled
#[cfg(feature = "validation")]
{
let validation_result = self.ui_state.validation.validate_field_content(
let _validation_result = self.ui_state.validation.validate_field_content(
field_index,
&value,
);
@@ -1396,7 +1432,7 @@ impl<D: DataProvider> FormEditor<D> {
// Validate the new content if validation is enabled
#[cfg(feature = "validation")]
{
let validation_result = self.ui_state.validation.validate_field_content(
let _validation_result = self.ui_state.validation.validate_field_content(
field_index,
&value,
);
@@ -1474,15 +1510,14 @@ impl<D: DataProvider> FormEditor<D> {
}
/// Cleanup cursor style (call this when shutting down)
#[cfg(feature = "cursor-style")]
pub fn cleanup_cursor(&self) -> std::io::Result<()> {
#[cfg(feature = "cursor-style")]
{
crate::canvas::CursorManager::reset()
}
#[cfg(not(feature = "cursor-style"))]
{
Ok(())
}
crate::canvas::CursorManager::reset()
}
#[cfg(not(feature = "cursor-style"))]
pub fn cleanup_cursor(&self) -> std::io::Result<()> {
Ok(())
}

View File

@@ -125,16 +125,33 @@ impl CharacterLimits {
position: usize,
character: char,
) -> Option<ValidationResult> {
// FIX: Actually simulate the insertion at the specified position
// This makes the `position` parameter essential to the logic
// 1. Create the new string by inserting the character at the correct position
let mut new_text = String::with_capacity(current_text.len() + character.len_utf8());
let mut chars = current_text.chars();
// Append characters from the original string that come before the insertion point
// We clamp the position to be safe
let clamped_pos = position.min(current_text.chars().count());
for _ in 0..clamped_pos {
if let Some(ch) = chars.next() {
new_text.push(ch);
}
}
// Insert the new character
new_text.push(character);
// Append the rest of the original string
for ch in chars {
new_text.push(ch);
}
// 2. Now perform all validation on the *actual* resulting text
let new_count = self.count(&new_text);
let current_count = self.count(current_text);
let char_count = match self.count_mode {
CountMode::Characters => 1,
CountMode::DisplayWidth => {
let char_str = character.to_string();
char_str.width()
},
CountMode::Bytes => character.len_utf8(),
};
let new_count = current_count + char_count;
// Check max length
if let Some(max) = self.max_length {