Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 25 additions & 195 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ categories = ["command-line-utilities", "development-tools", "data-structures",
exclude = ["/.github", "CHANGELOG.md", ".gitignore"]

[dependencies]
ratatui = "0.29.0"
ratatui = "0.24.0"
crossterm = "0.27.0"
calamine = "0.22.1"
anyhow = "1.0.79"
Expand All @@ -20,7 +20,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = "0.4"
indexmap = { version = "2.0", features = ["serde"] }
tui-textarea = "0.7.0"
tui-textarea = "0.4.0"

[profile.release]
opt-level = 3
Expand Down
11 changes: 8 additions & 3 deletions src/app/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::app::AppState;
use crate::app::InputMode;
use crate::app::{Transition, VimMode, VimState};
use anyhow::Result;
use ratatui::style::{Modifier, Style};
use tui_textarea::Input;

impl AppState<'_> {
Expand All @@ -11,10 +12,14 @@ impl AppState<'_> {
let content = self.get_cell_content(self.selected_cell.0, self.selected_cell.1);
self.input_buffer = content.clone();

self.text_area = tui_textarea::TextArea::default();
self.text_area.insert_str(&content);
self.text_area.set_tab_length(4);
// Initialize TextArea with content and settings
let mut text_area = tui_textarea::TextArea::default();
text_area.insert_str(&content);
text_area.set_tab_length(4);
text_area.set_cursor_line_style(Style::default());
text_area.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));

self.text_area = text_area;
self.vim_state = Some(VimState::new(VimMode::Normal));
}

Expand Down
1 change: 1 addition & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod state;
mod ui;
mod undo_manager;
mod vim;
mod word;

pub use state::*;
pub use vim::*;
17 changes: 15 additions & 2 deletions src/app/search.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
use crate::app::AppState;
use crate::app::InputMode;
use ratatui::style::{Modifier, Style};

impl AppState<'_> {
pub fn start_search_forward(&mut self) {
self.input_mode = InputMode::SearchForward;
self.input_buffer = String::new();
self.text_area = tui_textarea::TextArea::default();

// Initialize TextArea
let mut text_area = tui_textarea::TextArea::default();
text_area.set_cursor_line_style(Style::default());
text_area.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
self.text_area = text_area;

self.add_notification("Search forward mode".to_string());
self.highlight_enabled = true;
}

pub fn start_search_backward(&mut self) {
self.input_mode = InputMode::SearchBackward;
self.input_buffer = String::new();
self.text_area = tui_textarea::TextArea::default();

// Initialize TextArea
let mut text_area = tui_textarea::TextArea::default();
text_area.set_cursor_line_style(Style::default());
text_area.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
self.text_area = text_area;

self.add_notification("Search backward mode".to_string());
self.highlight_enabled = true;
}
Expand Down
3 changes: 1 addition & 2 deletions src/app/sheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,7 @@ impl AppState<'_> {

max_width = max_width.max(display_width);
}
let padding = (max_width / 5).max(2);
max_width + padding
max_width
}

pub fn get_column_width(&self, col: usize) -> usize {
Expand Down
36 changes: 34 additions & 2 deletions src/app/vim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use ratatui::widgets::Block;
use std::fmt;
use tui_textarea::{CursorMove, Input, Key, TextArea};

use crate::app::word::move_cursor_to_word_end;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VimMode {
Normal,
Expand Down Expand Up @@ -109,9 +111,39 @@ impl VimState {
ctrl: false,
..
} => {
textarea.move_cursor(CursorMove::WordEnd);
// Use custom WordEnd implementation
let lines = textarea.lines();
let (row, col) = textarea.cursor();
let (new_row, new_col) = move_cursor_to_word_end(lines, row, col);

// Set the cursor to the new position
if row != new_row {
// If need to move to a different row
while textarea.cursor().0 < new_row {
textarea.move_cursor(CursorMove::Down);
}
textarea.move_cursor(CursorMove::Head);
while textarea.cursor().1 < new_col {
textarea.move_cursor(CursorMove::Forward);
}
} else {
// If staying on the same row
if col < new_col {
// Move forward
while textarea.cursor().1 < new_col {
textarea.move_cursor(CursorMove::Forward);
}
} else {
// Move backward
while textarea.cursor().1 > new_col {
textarea.move_cursor(CursorMove::Back);
}
}
}

// For operator mode, include the character under the cursor
if matches!(self.mode, VimMode::Operator(_)) {
textarea.move_cursor(CursorMove::Forward); // Include the text under the cursor
textarea.move_cursor(CursorMove::Forward);
}
}
Input {
Expand Down
69 changes: 69 additions & 0 deletions src/app/word.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Custom implementation of word navigation functions from tui-textarea v0.5.2+

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CharKind {
Space,
Punctuation,
Other,
}

impl CharKind {
fn new(c: char) -> Self {
if c.is_whitespace() {
Self::Space
} else if c.is_ascii_punctuation() {
Self::Punctuation
} else {
Self::Other
}
}
}

/// Find the end of the next word
/// This is a custom implementation of the `find_word_end_next` function from tui-textarea v0.5.2+
pub fn find_word_end_next(line: &str, start_col: usize) -> Option<usize> {
let mut it = line.chars().enumerate().skip(start_col);
let (mut cur_col, cur_char) = it.next()?;
let mut cur = CharKind::new(cur_char);

for (next_col, c) in it {
let next = CharKind::new(c);
// if cursor started at the end of a word, don't stop
if next_col.saturating_sub(start_col) > 1 && cur != CharKind::Space && next != cur {
return Some(next_col.saturating_sub(1));
}
cur = next;
cur_col = next_col;
}

// if end of line is whitespace, don't stop the cursor
if cur != CharKind::Space && cur_col.saturating_sub(start_col) >= 1 {
return Some(cur_col);
}

None
}

/// Move cursor to the end of the next word
pub fn move_cursor_to_word_end(text: &[String], row: usize, col: usize) -> (usize, usize) {
if row >= text.len() {
return (row, col);
}

let line = &text[row];

if let Some(new_col) = find_word_end_next(line, col) {
return (row, new_col);
} else if row + 1 < text.len() {
// Try to find word end in the next line
if let Some(new_col) = find_word_end_next(&text[row + 1], 0) {
return (row + 1, new_col);
} else if !text[row + 1].is_empty() {
// If no word end found but line is not empty, go to the end of the line
return (row + 1, text[row + 1].chars().count().saturating_sub(1));
}
}

// Can't find a word end, stay at the current position
(row, col)
}
Loading