Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
100 lines
2.5 KiB
Rust
100 lines
2.5 KiB
Rust
use log_viewer_core::types::LogLevel;
|
|
use unicode_width::UnicodeWidthChar;
|
|
|
|
pub(super) struct ViewportEntry {
|
|
pub(super) wrapped_rows: Vec<String>,
|
|
pub(super) level: Option<LogLevel>,
|
|
pub(super) visual_height: usize,
|
|
}
|
|
|
|
pub(super) struct ViewportCache {
|
|
pub(super) entries: Vec<ViewportEntry>,
|
|
pub(super) logical_start: usize,
|
|
pub(super) width: usize,
|
|
json_format: bool,
|
|
pub(super) cached_total_visual_rows: Option<usize>,
|
|
}
|
|
|
|
impl ViewportCache {
|
|
pub(super) fn new() -> Self {
|
|
Self {
|
|
entries: Vec::new(),
|
|
logical_start: 0,
|
|
width: 0,
|
|
json_format: false,
|
|
cached_total_visual_rows: None,
|
|
}
|
|
}
|
|
|
|
pub(super) fn invalidate(&mut self) {
|
|
self.entries.clear();
|
|
self.logical_start = 0;
|
|
self.width = 0;
|
|
self.cached_total_visual_rows = None;
|
|
}
|
|
|
|
pub(super) fn needs_recompute(&self, width: usize, json_format: bool) -> bool {
|
|
self.width != width || self.json_format != json_format
|
|
}
|
|
|
|
pub(super) fn set_json_format(&mut self, json_format: bool) {
|
|
self.json_format = json_format;
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub(super) fn get_entry(&self, logical_line: usize) -> Option<&ViewportEntry> {
|
|
if logical_line >= self.logical_start {
|
|
let idx = logical_line - self.logical_start;
|
|
self.entries.get(idx)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(super) const TRUNCATE_TAB_WIDTH: usize = 4;
|
|
|
|
pub(super) fn gutter_width_for(total_lines: usize, is_loading: bool) -> usize {
|
|
if total_lines == 0 {
|
|
return 0;
|
|
}
|
|
let line_num_width = total_lines.to_string().len();
|
|
let loading_extra = if is_loading { 1 } else { 0 };
|
|
line_num_width + loading_extra + 1 + 1
|
|
}
|
|
|
|
pub(super) fn truncate_to_columns(s: &str, max_cols: usize) -> String {
|
|
if max_cols == 0 || s.is_empty() {
|
|
return String::new();
|
|
}
|
|
|
|
let mut out = String::new();
|
|
let mut col = 0;
|
|
|
|
for ch in s.chars() {
|
|
if ch == '\t' {
|
|
let tab_stop = TRUNCATE_TAB_WIDTH - (col % TRUNCATE_TAB_WIDTH);
|
|
if col + tab_stop > max_cols {
|
|
break;
|
|
}
|
|
for _ in 0..tab_stop {
|
|
out.push(' ');
|
|
}
|
|
col += tab_stop;
|
|
} else {
|
|
let w = if ch.is_control() {
|
|
0
|
|
} else {
|
|
ch.width().unwrap_or(0)
|
|
};
|
|
if col + w > max_cols {
|
|
break;
|
|
}
|
|
out.push(ch);
|
|
col += w;
|
|
}
|
|
}
|
|
|
|
out
|
|
}
|