refactor(tui): split remaining app.rs into per-concern submodules
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
use log_viewer_core::io::file_reader::AppendStatus;
|
||||
use log_viewer_core::io::progressive_reader::{ReaderState, compute_line_visual_height};
|
||||
use log_viewer_core::watcher::file_watcher::FileEvent;
|
||||
|
||||
use super::viewport_cache::gutter_width_for;
|
||||
use super::{App, AppLoadingState};
|
||||
|
||||
impl App {
|
||||
pub fn poll_file_watcher(&mut self) {
|
||||
let events: Vec<FileEvent> = match &mut self.file_watcher {
|
||||
Some(w) => std::iter::from_fn(|| w.try_recv()).collect(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
FileEvent::Appended { new_size: _ } => {
|
||||
self.handle_file_appended();
|
||||
}
|
||||
FileEvent::Truncated { new_size: _ } => {
|
||||
self.handle_file_truncated();
|
||||
}
|
||||
FileEvent::Rotated { new_inode: _ } => {
|
||||
// Don't auto-switch; old content preserved.
|
||||
// User can reload manually if desired.
|
||||
}
|
||||
FileEvent::Removed => {
|
||||
self.loading_state = AppLoadingState::Error("File has been deleted".into());
|
||||
}
|
||||
FileEvent::WatcherError { message: _ } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_file_appended(&mut self) {
|
||||
let rebased = self.rebase_offset_for_invalidate();
|
||||
match &mut self.loading_state {
|
||||
AppLoadingState::Ready { reader } => {
|
||||
let old_reader_line_count = reader.line_count();
|
||||
let status = reader.update_for_append();
|
||||
let width = {
|
||||
let total = if self.content_width > 0 {
|
||||
self.content_width as usize
|
||||
} else {
|
||||
80
|
||||
};
|
||||
total.saturating_sub(gutter_width_for(reader.line_count(), false))
|
||||
};
|
||||
match status {
|
||||
Ok(AppendStatus::Appended(_new_lines)) => {
|
||||
let _ = reader.save_cache();
|
||||
|
||||
let (old_line_count, can_extend) = {
|
||||
match &reader.state {
|
||||
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 && old_line_count == old_reader_line_count {
|
||||
if let ReaderState::Ready {
|
||||
visual_height_index: Some(index),
|
||||
reader: fr,
|
||||
} = &mut reader.state
|
||||
{
|
||||
if old_line_count > 0 {
|
||||
let last_old_line_text =
|
||||
fr.get_line(old_line_count - 1).unwrap_or("");
|
||||
let new_h = compute_line_visual_height(
|
||||
last_old_line_text,
|
||||
width,
|
||||
self.json_format,
|
||||
);
|
||||
index.replace_last_line_height(new_h);
|
||||
}
|
||||
let mut new_heights = Vec::with_capacity(
|
||||
new_line_count.saturating_sub(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 {
|
||||
let (new_offset, new_sub) = rebased;
|
||||
self.v_offset = new_offset;
|
||||
self.v_sub_offset = new_sub;
|
||||
self.cursor_sub_offset = 0;
|
||||
reader.invalidate_visual_height_index();
|
||||
reader.start_visual_height_rebuild(width, self.json_format);
|
||||
}
|
||||
|
||||
self.viewport_cache.invalidate();
|
||||
}
|
||||
Ok(AppendStatus::Reloaded) => {
|
||||
let _ = reader.save_cache();
|
||||
let (new_offset, _new_sub) = rebased;
|
||||
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_offset = new_offset;
|
||||
self.v_sub_offset = 0;
|
||||
self.cursor_sub_offset = 0;
|
||||
self.viewport_cache.invalidate();
|
||||
self.clamp_v_offset();
|
||||
}
|
||||
Ok(AppendStatus::Unchanged) | Err(_) => {}
|
||||
}
|
||||
}
|
||||
AppLoadingState::Loading { .. } => {
|
||||
self.reload_after_loading = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn reload_ready_reader(&mut self) {
|
||||
let (new_offset, _new_sub) = self.rebase_offset_for_invalidate();
|
||||
if let AppLoadingState::Ready { reader } = &mut self.loading_state {
|
||||
let _ = reader.reload();
|
||||
let width = {
|
||||
let total = if self.content_width > 0 {
|
||||
self.content_width as usize
|
||||
} else {
|
||||
80
|
||||
};
|
||||
total.saturating_sub(gutter_width_for(reader.line_count(), false))
|
||||
};
|
||||
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_offset = new_offset;
|
||||
self.v_sub_offset = 0;
|
||||
self.clamp_cursor_sub_offset_no_vhi();
|
||||
self.viewport_cache.invalidate();
|
||||
self.clamp_v_offset();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_file_truncated(&mut self) {
|
||||
match &mut self.loading_state {
|
||||
AppLoadingState::Ready { .. } => {
|
||||
self.reload_ready_reader();
|
||||
}
|
||||
AppLoadingState::Loading { .. } => {
|
||||
self.reload_after_loading = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user