use ansi_term::ANSIStrings; use crate::{LinePart, ARROW_SEPARATOR}; use zellij_tile::prelude::*; use zellij_tile_utils::style; fn get_current_title_len(current_title: &[LinePart]) -> usize { current_title .iter() .fold(0, |acc, title_part| acc + title_part.len) } fn populate_tabs_in_tab_line( tabs_before_active: &mut Vec, tabs_after_active: &mut Vec, tabs_to_render: &mut Vec, cols: usize, ) { let mut take_next_tab_from_tabs_after = true; loop { if tabs_before_active.is_empty() && tabs_after_active.is_empty() { break; } let current_title_len = get_current_title_len(&tabs_to_render); if current_title_len >= cols { break; } let should_take_next_tab = take_next_tab_from_tabs_after; let can_take_next_tab = !tabs_after_active.is_empty() && tabs_after_active.get(0).unwrap().len + current_title_len <= cols; let can_take_previous_tab = !tabs_before_active.is_empty() && tabs_before_active.last().unwrap().len + current_title_len <= cols; if should_take_next_tab && can_take_next_tab { let next_tab = tabs_after_active.remove(0); tabs_to_render.push(next_tab); take_next_tab_from_tabs_after = false; } else if can_take_previous_tab { let previous_tab = tabs_before_active.pop().unwrap(); tabs_to_render.insert(0, previous_tab); take_next_tab_from_tabs_after = true; } else if can_take_next_tab { let next_tab = tabs_after_active.remove(0); tabs_to_render.push(next_tab); take_next_tab_from_tabs_after = false; } else { break; } } } fn left_more_message(tab_count_to_the_left: usize, palette: Palette, separator: &str) -> LinePart { if tab_count_to_the_left == 0 { return LinePart { part: String::new(), len: 0, }; } let more_text = if tab_count_to_the_left < 10000 { format!(" ← +{} ", tab_count_to_the_left) } else { " ← +many ".to_string() }; // 238 let more_text_len = more_text.chars().count() + 2; // 2 for the arrows let left_separator = style!(palette.cyan, palette.orange).paint(separator); let more_styled_text = style!(palette.black, palette.orange) .bold() .paint(more_text); let right_separator = style!(palette.orange, palette.cyan).paint(separator); let more_styled_text = format!( "{}", ANSIStrings(&[left_separator, more_styled_text, right_separator,]) ); LinePart { part: more_styled_text, len: more_text_len, } } fn right_more_message( tab_count_to_the_right: usize, palette: Palette, separator: &str, ) -> LinePart { if tab_count_to_the_right == 0 { return LinePart { part: String::new(), len: 0, }; }; let more_text = if tab_count_to_the_right < 10000 { format!(" +{} → ", tab_count_to_the_right) } else { " +many → ".to_string() }; let more_text_len = more_text.chars().count() + 1; // 2 for the arrow let left_separator = style!(palette.cyan, palette.orange).paint(separator); let more_styled_text = style!(palette.black, palette.orange) .bold() .paint(more_text); let right_separator = style!(palette.orange, palette.cyan).paint(separator); let more_styled_text = format!( "{}", ANSIStrings(&[left_separator, more_styled_text, right_separator,]) ); LinePart { part: more_styled_text, len: more_text_len, } } fn add_previous_tabs_msg( tabs_before_active: &mut Vec, tabs_to_render: &mut Vec, title_bar: &mut Vec, cols: usize, palette: Palette, separator: &str, ) { while get_current_title_len(&tabs_to_render) + left_more_message(tabs_before_active.len(), palette, separator).len >= cols { tabs_before_active.push(tabs_to_render.remove(0)); } let left_more_message = left_more_message(tabs_before_active.len(), palette, separator); title_bar.push(left_more_message); } fn add_next_tabs_msg( tabs_after_active: &mut Vec, title_bar: &mut Vec, cols: usize, palette: Palette, separator: &str, ) { while get_current_title_len(&title_bar) + right_more_message(tabs_after_active.len(), palette, separator).len >= cols { tabs_after_active.insert(0, title_bar.pop().unwrap()); } let right_more_message = right_more_message(tabs_after_active.len(), palette, separator); title_bar.push(right_more_message); } fn tab_line_prefix(palette: Palette) -> LinePart { let prefix_text = " Zellij ".to_string(); let prefix_text_len = prefix_text.chars().count(); let prefix_styled_text = style!(palette.white, palette.cyan) .bold() .paint(prefix_text); LinePart { part: format!("{}", prefix_styled_text), len: prefix_text_len, } } pub fn tab_separator(capabilities: PluginCapabilities) -> &'static str { if !capabilities.arrow_fonts { ARROW_SEPARATOR } else { &"" } } pub fn tab_line( mut all_tabs: Vec, active_tab_index: usize, cols: usize, palette: Palette, capabilities: PluginCapabilities, ) -> Vec { let mut tabs_to_render: Vec = vec![]; let mut tabs_after_active = all_tabs.split_off(active_tab_index); let mut tabs_before_active = all_tabs; let active_tab = if !tabs_after_active.is_empty() { tabs_after_active.remove(0) } else { tabs_before_active.pop().unwrap() }; tabs_to_render.push(active_tab); let prefix = tab_line_prefix(palette); populate_tabs_in_tab_line( &mut tabs_before_active, &mut tabs_after_active, &mut tabs_to_render, cols - prefix.len, ); let mut tab_line: Vec = vec![]; if !tabs_before_active.is_empty() { add_previous_tabs_msg( &mut tabs_before_active, &mut tabs_to_render, &mut tab_line, cols - prefix.len, palette, tab_separator(capabilities), ); } tab_line.append(&mut tabs_to_render); if !tabs_after_active.is_empty() { add_next_tabs_msg( &mut tabs_after_active, &mut tab_line, cols - prefix.len, palette, tab_separator(capabilities), ); } tab_line.insert(0, prefix); tab_line }