diff --git a/src/app.rs b/src/app.rs index 1845e5d..6af84a7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -251,7 +251,7 @@ impl App { let csv_table_state = CsvTableState::new( original_filename, - rows_view.headers().len(), + rows_view.raw_headers().len(), &echo_column, ignore_case, color_columns, @@ -696,7 +696,7 @@ impl App { } self.csv_table_state - .set_total_cols(self.rows_view.headers().len()); + .set_total_cols(self.rows_view.raw_headers().len()); if let Some(f) = &self.finder { // TODO: need to create a new finder every time? @@ -1340,7 +1340,7 @@ mod tests { "4 │ 71 48 0 Worcester │ ", "5 │ 89 46 11 Wisconsin Dells │ ", "───┴─────────────────────────────────────────────┴──────────────────────────────", - "stdin [Row 1/128, Col 1/4] [Filter \"Lon|City\": 4/10 cols] ", + "stdin [Row 1/128, Col 5/10] [Filter \"Lon|City\": 1/4 cols] ", ]; let actual_buffer = terminal.backend().buffer().clone(); let lines = to_lines(&actual_buffer); @@ -1370,7 +1370,7 @@ mod tests { " │ │ ", " │ │ ", "───┴───────────┴────────────────────────────────────────────────────────────────", - "stdin [Row 1/2, Col 1/1] [Filter \"COL2\": 1/2 cols] ", + "stdin [Row 1/2, Col 2/2] [Filter \"COL2\": 1/1 cols] ", ]; let actual_buffer = terminal.backend().buffer().clone(); let lines = to_lines(&actual_buffer); @@ -1433,7 +1433,7 @@ mod tests { "4 │ Worcester MA │ ", "5 │ Wisconsin Dells WI │ ", "───┴──────────────────────────────┴─────────────────────────────────────────────", - "stdin [Row 1/128, Col 1/2] [Filter \"(?i)city|state|wa\": 2/10 cols] [ignore-case]", + "stdin [Row 1/128, Col 9/10] [Filter \"(?i)city|state|wa\": 1/2 cols] [ignore-case]", ]; let actual_buffer = terminal.backend().buffer().clone(); let lines = to_lines(&actual_buffer); @@ -2022,7 +2022,7 @@ mod tests { " │ │ ", " │ │ ", "────┴────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────", - "stdin [Row 97/128, Col 1/1] [Filter \"Salt Lake City\": 1/1] [Filter \"City\": 1/10 cols] ", + "stdin [Row 97/128, Col 9/10] [Filter \"Salt Lake City\": 1/1] [Filter \"City\": 1/1 cols] ", ]; let actual_buffer = terminal.backend().buffer().clone(); let lines = to_lines(&actual_buffer); @@ -2164,7 +2164,7 @@ mod tests { "4 │ 42 … 12 N 71 48 0 W Worce… ", "5 │ 43 … 48 N 89 46 11 W Wisco… ", "───┴────────────────────────────────────────────────────────────────────────────", - "stdin [Row 1/128, Col 1/10] ", + "stdin [Row 1/128, Col 2/10] ", ]; assert_eq!(lines, expected); @@ -2187,7 +2187,7 @@ mod tests { "4 │ 42 … 12 │ ", "5 │ 43 … 48 │ ", "───┴──────────────────────────────┴─────────────────────────────────────────────", - "stdin [Row 1/128, Col 1/3] [Filter \"Lat\": 3/10 cols] ", + "stdin [Row 1/128, Col 2/10] [Filter \"Lat\": 2/3 cols] ", ]; assert_eq!(lines, expected); @@ -2205,7 +2205,7 @@ mod tests { "4 │ 42 16 12 N 71 48 0 W Worcester ", "5 │ 43 37 48 N 89 46 11 W Wisconsin… ", "───┴────────────────────────────────────────────────────────────────────────────", - "stdin [Row 1/128, Col 1/10] ", + "stdin [Row 1/128, Col 2/10] ", ]; assert_eq!(lines, expected); } @@ -2239,7 +2239,7 @@ mod tests { "125 │ 50 25 11 N 104 39 0 W Regina SA │ ", "124 │ 39 31 12 N 119 48 35 W Reno NV │ ", "─────┴─────────────────────────────────────────────────────────────────────────────────────┴────────", - "stdin [Row 128/128, Col 1/10] ", + "stdin [Row 128/128, Col 9/10] ", ]; assert_eq!(lines, expected); @@ -2258,7 +2258,7 @@ mod tests { "4 │ 42 16 12 N 71 48 0 W Worcester MA │ ", "5 │ 43 37 48 N 89 46 11 W Wisconsin Dells WI │ ", "───┴──────────────────────────────────────────────────────────────────────────────────────────┴─────", - "stdin [Row 1/128, Col 1/10] ", + "stdin [Row 1/128, Col 9/10] ", ]; assert_eq!(lines, expected); } @@ -2413,7 +2413,7 @@ mod tests { "93 │ 32 42 35 San Diego │ ", "91 │ 37 46 47 San Francisco │ ", "────┴────────────────────────────────────────────┴──────────────────────────────", - "stdin [Row 96/128, Col 1/4] [Filter \"San\": 1/11] [Filter \"Lat|City\": 4/10 cols] ", + "stdin [Row 96/128, Col 1/10] [Filter \"San\": 1/11] [Filter \"Lat|City\": 1/4 cols] ", ]; assert_eq!(lines, expected); @@ -2434,7 +2434,7 @@ mod tests { "89 │ 33 45 35 Santa Ana │ ", "92 │ 41 27 0 Sandusky │ ", "────┴───────────────────────────────────────────┴───────────────────────────────", - "stdin [Row 86/128, Col 1/4] [Filter \"San\": -/11] [Filter \"Lat|City\": 4/10 cols] ", + "stdin [Row 86/128, Col 9/10] [Filter \"San\": -/11] [Filter \"Lat|City\": 4/4 cols] ", ]; assert_eq!(lines, expected); } @@ -2494,7 +2494,7 @@ mod tests { "4 │ 42 16 12 │ ", "5 │ 43 37 48 │ ", "───┴──────────────────────────┴─────────────────────────────────────────────────", - "stdin [Row 1/128, Col 1/3] [Filter \"Lat\": 3/10 cols] ", + "stdin [Row 1/128, Col 1/10] [Filter \"Lat\": 1/3 cols] ", ]; let actual_buffer = terminal.backend().buffer().clone(); let lines = to_lines(&actual_buffer); @@ -2557,7 +2557,7 @@ mod tests { "12 │ 41 15 0 N 77 0 0 W Williams… ", "20 │ 31 13 11 N 82 20 59 W Waycross ", "────┴───────────────────────────────────────────────────────────────────────────", - "stdin [Row 4/128, Col 1/10] [Filter \"^1\" in LatM: -/19] ", + "stdin [Row 4/128, Col 2/10] [Filter \"^1\" in LatM: -/19] ", ]; assert_eq!(lines, expected); } @@ -2595,7 +2595,7 @@ mod tests { "65 │ 11 N 83 48 35 W Springfield OH │ ", "92 │ 0 N 82 42 35 W Sandusky OH │ ", "────┴───────────────────────────────────────────────────────────────────────┴───", - "stdin [Row 1/128, Col 3/10] [Filter \"^OH$\" in State: 1/6] ", + "stdin [Row 1/128, Col 10/10] [Filter \"^OH$\" in State: 1/6] ", ]; assert_eq!(lines, expected); } @@ -2804,7 +2804,7 @@ mod tests { " │ │ ", " │ │ ", "───┴──────────┴───────────────────────────────────", - "stdin [Row 1/2, Col 1/1] [Filter \"x1\": 1/1] [Filte", + "stdin [Row 1/2, Col 1/2] [Filter \"x1\": 1/1] [Filte", ]; let actual_buffer = terminal.backend().buffer().clone(); let lines = to_lines(&actual_buffer); @@ -2841,7 +2841,7 @@ mod tests { "4 │ 42 16 ║ W Worcester MA ", "5 │ 43 37 ║ W Wisconsi… WI ", "───┴────────────────╨─────────────────────────────", - "stdin [Row 1/128, Col 6/10] ", + "stdin [Row 1/128, Col 8/10] ", ]; assert_eq!(lines, expected); } @@ -2872,7 +2872,7 @@ mod tests { "4 │ N Worcester MA │ ", "5 │ N Wisconsin Dells WI │ ", "───┴────────────────────────────────────┴─────────", - "Select your city! [Row 1/128, Col 1/3] [Filter \"NS", + "Select your city! [Row 1/128, Col 4/10] [Filter \"N", ]; assert_eq!(lines, expected); } diff --git a/src/columns_filter.rs b/src/columns_filter.rs index 8d550fc..bfbb00d 100644 --- a/src/columns_filter.rs +++ b/src/columns_filter.rs @@ -6,7 +6,6 @@ pub struct ColumnsFilter { indices: Vec, filtered_headers: Vec, filtered_flags: Vec, - num_columns_before_filter: usize, disabled_because_no_match: bool, } @@ -37,7 +36,6 @@ impl ColumnsFilter { indices, filtered_headers, filtered_flags, - num_columns_before_filter: headers.len(), disabled_because_no_match, } } @@ -58,10 +56,6 @@ impl ColumnsFilter { self.indices.len() } - pub fn num_original(&self) -> usize { - self.num_columns_before_filter - } - pub fn disabled_because_no_match(&self) -> bool { self.disabled_because_no_match } diff --git a/src/ui.rs b/src/ui.rs index eca709c..02e576a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -799,12 +799,67 @@ impl<'a> CsvTable<'a> { Some(row) => row.record_num.to_string(), _ => "-".to_owned(), }; + + // Show the selected column when: + // - Line wrap is off + // - We're in cell or column selection mode + // Otherwise fall back to the scroll offset. + let use_selection_col = !state.enable_line_wrap + && matches!( + state.selection.as_ref().map(|s| s.selection_type()), + Some(view::SelectionType::Cell | view::SelectionType::Column) + ); + + let current_col = if let Some(selection) = &state.selection { + match selection.selection_type() { + view::SelectionType::Column | view::SelectionType::Cell + if use_selection_col => + { + if let Some(col_idx) = selection.column.index() { + // Get the filtered column index, then map to origin index + let filtered_col_idx = + state.cols_offset.get_filtered_column_index(col_idx) as usize; + // Map to original column position (1-indexed) + let origin_col = self + .header + .get(filtered_col_idx) + .map(|h| h.origin_index + 1) + .unwrap_or(filtered_col_idx + 1); + std::cmp::min(origin_col as u64, state.total_cols as u64) + } else { + // When no column is selected, show the first visible non-frozen column origin index + let filtered_col_idx = (state.cols_offset.num_freeze + + state.cols_offset.num_skip) + as usize; + self.header + .get(filtered_col_idx) + .map(|h| (h.origin_index + 1) as u64) + .unwrap_or((filtered_col_idx + 1) as u64) + } + } + _ => { + // In row selection mode or when line wrap is on, show the first visible non-frozen column origin index + let filtered_col_idx = + (state.cols_offset.num_freeze + state.cols_offset.num_skip) as usize; + self.header + .get(filtered_col_idx) + .map(|h| (h.origin_index + 1) as u64) + .unwrap_or((filtered_col_idx + 1) as u64) + } + } + } else { + // When no selection, show the first visible non-frozen column origin index + let filtered_col_idx = + (state.cols_offset.num_freeze + state.cols_offset.num_skip) as usize; + self.header + .get(filtered_col_idx) + .map(|h| (h.origin_index + 1) as u64) + .unwrap_or((filtered_col_idx + 1) as u64) + }; + content += format!( " [Row {}/{}, Col {}/{}]", - row_num, - total_str, - state.cols_offset.num_skip + 1, - state.total_cols, + row_num, total_str, current_col, state.total_cols, ) .as_str(); @@ -818,7 +873,28 @@ impl<'a> CsvTable<'a> { } // Filter columns - if let FilterColumnsState::Enabled(info) = &state.filter_columns_state { + if let FilterColumnsState::Enabled(mut info) = state.filter_columns_state.clone() { + // Calculate the current filtered column index (1-indexed) + let filtered_col_idx = if let Some(selection) = &state.selection { + match selection.selection_type() { + view::SelectionType::Column | view::SelectionType::Cell + if use_selection_col => + { + if let Some(col_idx) = selection.column.index() { + state.cols_offset.get_filtered_column_index(col_idx) as usize + 1 + } else { + (state.cols_offset.num_freeze + state.cols_offset.num_skip) as usize + + 1 + } + } + _ => { + (state.cols_offset.num_freeze + state.cols_offset.num_skip) as usize + 1 + } + } + } else { + (state.cols_offset.num_freeze + state.cols_offset.num_skip) as usize + 1 + }; + info.current_filtered_col = filtered_col_idx; content += format!(" {}", info.status_line()).as_str(); } @@ -1136,6 +1212,7 @@ impl FinderActiveState { } } +#[derive(Clone)] pub enum FilterColumnsState { Disabled, Enabled(FilterColumnsInfo), @@ -1147,8 +1224,8 @@ impl FilterColumnsState { Self::Enabled(FilterColumnsInfo { pattern: columns_filter.pattern(), shown: columns_filter.num_filtered(), - total: columns_filter.num_original(), disabled_because_no_match: columns_filter.disabled_because_no_match(), + current_filtered_col: 1, // Will be updated when rendering status bar }) } else { Self::Disabled @@ -1156,11 +1233,12 @@ impl FilterColumnsState { } } +#[derive(Clone)] pub struct FilterColumnsInfo { pattern: Regex, shown: usize, - total: usize, disabled_because_no_match: bool, + current_filtered_col: usize, } impl FilterColumnsInfo { @@ -1170,7 +1248,7 @@ impl FilterColumnsInfo { if self.disabled_because_no_match { line += "no match, showing all columns]"; } else { - line += format!("{}/{} cols]", self.shown, self.total).as_str(); + line += format!("{}/{} cols]", self.current_filtered_col, self.shown).as_str(); } line }