diff --git a/crates/core/src/io/file_reader.rs b/crates/core/src/io/file_reader.rs index 79f7a1d..fa1725b 100644 --- a/crates/core/src/io/file_reader.rs +++ b/crates/core/src/io/file_reader.rs @@ -9,6 +9,14 @@ use crate::io::index_cache::IndexCache; use crate::io::line_index::LineIndex; use std::path::{Path, PathBuf}; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AppendStatus { + Unchanged, + Appended(u64), + /// File shrank; mmap and index rebuilt via reload(). + Reloaded, +} + pub struct FileReader { path: PathBuf, mmap: Option, @@ -108,13 +116,17 @@ impl FileReader { Ok(()) } - pub fn update_for_append(&mut self) -> Result { + pub fn update_for_append(&mut self) -> Result { let file = std::fs::File::open(&self.path)?; let new_size = file.metadata()?.len(); let old_size = self.mmap.as_ref().map_or(0u64, |m| m.len() as u64); - if new_size <= old_size { - return Ok(0); + if new_size < old_size { + self.reload()?; + return Ok(AppendStatus::Reloaded); + } + if new_size == old_size { + return Ok(AppendStatus::Unchanged); } let old_lines = self.line_index.line_count() as u64; @@ -127,7 +139,9 @@ impl FileReader { .extend_from_bytes(&mmap[old_size as usize..], old_size); self.mmap = Some(mmap); - Ok(self.line_index.line_count() as u64 - old_lines) + Ok(AppendStatus::Appended( + self.line_index.line_count() as u64 - old_lines, + )) } } @@ -305,8 +319,8 @@ mod tests { file.write_all(b"ccc\nddd\n").unwrap(); } - let new_lines = reader.update_for_append().unwrap(); - assert_eq!(new_lines, 2); + let status = reader.update_for_append().unwrap(); + assert_eq!(status, AppendStatus::Appended(2)); assert_eq!(reader.line_count(), 4); assert_eq!(reader.get_line(0), Some("aaa")); assert_eq!(reader.get_line(1), Some("bbb")); @@ -320,8 +334,8 @@ mod tests { let mut reader = FileReader::open(f.path()).unwrap(); assert_eq!(reader.line_count(), 1); - let new_lines = reader.update_for_append().unwrap(); - assert_eq!(new_lines, 0); + let status = reader.update_for_append().unwrap(); + assert_eq!(status, AppendStatus::Unchanged); assert_eq!(reader.line_count(), 1); } @@ -341,8 +355,11 @@ mod tests { file.write_all(b"x\n").unwrap(); } - let new_lines = reader.update_for_append().unwrap(); - assert_eq!(new_lines, 0); + let status = reader.update_for_append().unwrap(); + assert_eq!(status, AppendStatus::Reloaded); + assert_eq!(reader.line_count(), 1); + assert_eq!(reader.file_size(), 2); + assert_eq!(reader.get_line(0), Some("x")); } #[test] diff --git a/crates/core/src/io/progressive_reader.rs b/crates/core/src/io/progressive_reader.rs index 62e8ee9..bbb9dcf 100644 --- a/crates/core/src/io/progressive_reader.rs +++ b/crates/core/src/io/progressive_reader.rs @@ -3,7 +3,7 @@ use std::fmt; use std::path::{Path, PathBuf}; use crate::error::{CoreError, Result}; -use crate::io::file_reader::FileReader; +use crate::io::file_reader::{AppendStatus, FileReader}; use crate::io::index_cache::IndexCache; use crate::io::line_index::LineIndex; use crate::io::line_sampler::sample_line_count; @@ -656,10 +656,10 @@ impl ProgressiveFileReader { } } - pub fn update_for_append(&mut self) -> Result { + pub fn update_for_append(&mut self) -> Result { match &mut self.state { ReaderState::Ready { reader, .. } => reader.update_for_append(), - _ => Ok(0), + _ => Ok(AppendStatus::Unchanged), } } diff --git a/crates/tui/src/app.rs b/crates/tui/src/app.rs index 9abca76..e953556 100644 --- a/crates/tui/src/app.rs +++ b/crates/tui/src/app.rs @@ -859,43 +859,58 @@ impl App { let width = self.get_content_width(); match &mut self.loading_state { AppLoadingState::Ready { reader } => { - if let Ok(_new_lines) = reader.update_for_append() { - let _ = reader.save_cache(); + let status = reader.update_for_append(); + match status { + Ok( + log_viewer_core::io::file_reader::AppendStatus::Appended(_new_lines), + ) => { + let _ = reader.save_cache(); - let (old_line_count, can_extend) = { - match &reader.state { - log_viewer_core::io::progressive_reader::ReaderState::Ready { - visual_height_index: Some(idx), - .. - } => (idx.line_count(), idx.is_valid_for(self.json_format, width)), - _ => (0, false), - } - }; - let new_line_count = reader.line_count(); - - if can_extend && new_line_count > old_line_count { - if let log_viewer_core::io::progressive_reader::ReaderState::Ready { - visual_height_index: Some(index), - reader: fr, - } = &mut reader.state - { - let mut new_heights = Vec::with_capacity(new_line_count - old_line_count); - for i in old_line_count..new_line_count { - let line_text = fr.get_line(i).unwrap_or(""); - new_heights.push(compute_line_visual_height( - line_text, - width, - self.json_format, - )); + let (old_line_count, can_extend) = { + match &reader.state { + log_viewer_core::io::progressive_reader::ReaderState::Ready { + visual_height_index: Some(idx), + .. + } => (idx.line_count(), idx.is_valid_for(self.json_format, width)), + _ => (0, false), } - index.extend_from_heights(&new_heights); + }; + let new_line_count = reader.line_count(); + + if can_extend && new_line_count > old_line_count { + if let log_viewer_core::io::progressive_reader::ReaderState::Ready { + visual_height_index: Some(index), + reader: fr, + } = &mut reader.state + { + let mut new_heights = Vec::with_capacity(new_line_count - old_line_count); + for i in old_line_count..new_line_count { + let line_text = fr.get_line(i).unwrap_or(""); + new_heights.push(compute_line_visual_height( + line_text, + width, + self.json_format, + )); + } + index.extend_from_heights(&new_heights); + } + } else { + reader.invalidate_visual_height_index(); + reader.start_visual_height_rebuild(width, self.json_format); } - } else { + + self.viewport_cache.invalidate(); + } + Ok(log_viewer_core::io::file_reader::AppendStatus::Reloaded) => { + let _ = reader.save_cache(); reader.invalidate_visual_height_index(); reader.start_visual_height_rebuild(width, self.json_format); + self.cursor_line = self.cursor_line.min(self.total_lines().saturating_sub(1)); + self.v_sub_offset = 0; + self.viewport_cache.invalidate(); + self.clamp_v_offset(); } - - self.viewport_cache.invalidate(); + Ok(log_viewer_core::io::file_reader::AppendStatus::Unchanged) | Err(_) => {} } } _ => {} @@ -911,7 +926,9 @@ impl App { reader.invalidate_visual_height_index(); reader.start_visual_height_rebuild(width, self.json_format); self.cursor_line = self.cursor_line.min(self.total_lines().saturating_sub(1)); + self.v_sub_offset = 0; self.viewport_cache.invalidate(); + self.clamp_v_offset(); } _ => {} }