diff --git a/README.md b/README.md index 940da05..88bdc9f 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Key | Action `Ctrl + l` | Scroll one window right `Ctrl + ←` | Scroll left to first column `Ctrl + →` | Scroll right to last column +`Ctrl + e` | Print the marked lines to stdout and exit `G` (or `End`) | Go to bottom `g` (or `Home`) | Go to top `G` | Go to line `n` diff --git a/src/app.rs b/src/app.rs index 1845e5d..e9afe85 100644 --- a/src/app.rs +++ b/src/app.rs @@ -332,6 +332,11 @@ impl App { { return Ok(Some(result)); } + if matches!(control, Control::SelectMarks) + && let Some(result) = self.get_marked_rows() + { + return Ok(Some(result)); + } if matches!(control, Control::Help) { self.help_page_state.activate(); self.input_handler.enter_help_mode(); @@ -747,6 +752,27 @@ impl App { None } + fn get_marked_rows(&mut self) -> Option { + let marked = self.rows_view.marked_rows(); + if marked.is_empty() { + return None; + } + + let mut record_numbers: Vec = marked.iter().copied().collect(); + record_numbers.sort_unstable(); + + let headers_line = self.rows_view.get_headers_line(); + match self.rows_view.get_rows_values(&record_numbers) { + Ok(lines) => { + let mut content_lines = Vec::with_capacity(lines.len().saturating_add(1)); + content_lines.push(headers_line); + content_lines.extend(lines); + Some(content_lines.join("\n")) + } + Err(_) => None, + } + } + fn get_finder_starting_row_index(&self) -> usize { self.rows_view.selected_offset().unwrap_or(0) as usize } diff --git a/src/help.rs b/src/help.rs index 7fd9c82..c20ea81 100644 --- a/src/help.rs +++ b/src/help.rs @@ -55,6 +55,7 @@ r : Reset to default view (clear all filters and custom co H (or ?) : Display this help m : Mark / unmark the selected row visually M : Clear all row marks +Ctrl + m : Print the marked rows (with header) to stdout and exit q : Exit"; pub struct HelpPage {} diff --git a/src/input.rs b/src/input.rs index e14fa4c..ba95866 100644 --- a/src/input.rs +++ b/src/input.rs @@ -38,6 +38,7 @@ pub enum Control { BufferReset, Select, CopySelection, + SelectMarks, ToggleSelectionType, ToggleLineWrap(WrapMode), ToggleMark, @@ -196,6 +197,7 @@ impl InputHandler { KeyCode::Left => Control::ScrollLeftMost, KeyCode::Right => Control::ScrollRightMost, KeyCode::Char('j') => Control::ToggleNaturalSort, + KeyCode::Char('e') => Control::SelectMarks, _ => Control::Nothing, }, _ => Control::Nothing, diff --git a/src/view.rs b/src/view.rs index d95cb86..4b817f8 100644 --- a/src/view.rs +++ b/src/view.rs @@ -367,6 +367,34 @@ impl RowsView { None } + pub fn get_headers_line(&self) -> String { + self.headers() + .iter() + .map(|h| h.name.clone()) + .collect::>() + .join("\t") + } + + pub fn get_rows_values(&mut self, record_numbers: &[usize]) -> CsvlensResult> { + if record_numbers.is_empty() { + return Ok(vec![]); + } + + // marked rows store 1-based record numbers; convert to 0-based indices for fetching + let indices: Vec = record_numbers + .iter() + .map(|&n| n.saturating_sub(1) as u64) + .collect(); + + let (mut rows, _) = self.reader.get_rows_for_indices(&indices)?; + + if let Some(columns_filter) = &self.columns_filter { + rows = Self::subset_columns(&rows, columns_filter.indices()); + } + + Ok(rows.into_iter().map(|row| row.fields.join("\t")).collect()) + } + pub fn num_rows(&self) -> u64 { self.num_rows }