use super::tab::EditorTabState;
#[derive(Debug)]
pub struct AppState {
tabs: Vec<EditorTabState>,
active_tab_index: Option<usize>,
next_tab_id: usize,
}
impl Default for AppState {
fn default() -> Self {
Self::new()
}
}
impl AppState {
pub fn new() -> Self {
Self {
tabs: Vec::new(),
active_tab_index: None,
next_tab_id: 0,
}
}
pub fn open_new_tab(&mut self, file_name: impl Into<String>) -> usize {
let id = self.next_tab_id;
self.next_tab_id += 1;
let tab = EditorTabState::new(id, file_name);
self.tabs.push(tab);
if self.active_tab_index.is_none() {
self.active_tab_index = Some(0);
}
id
}
pub fn close_tab(&mut self, index: usize) -> bool {
if index >= self.tabs.len() {
return false;
}
self.tabs.remove(index);
if let Some(active_idx) = self.active_tab_index {
if self.tabs.is_empty() {
self.active_tab_index = None;
} else if active_idx >= self.tabs.len() {
self.active_tab_index = Some(self.tabs.len() - 1);
} else if active_idx == index && self.tabs.len() > index {
self.active_tab_index = Some(index);
} else if active_idx > index {
self.active_tab_index = Some(active_idx - 1);
}
}
true
}
pub fn active_tab(&self) -> Option<&EditorTabState> {
self.active_tab_index.and_then(|idx| self.tabs.get(idx))
}
#[allow(dead_code)]
pub fn active_tab_mut(&mut self) -> Option<&mut EditorTabState> {
self.active_tab_index.and_then(|idx| self.tabs.get_mut(idx))
}
pub fn set_active_tab(&mut self, index: usize) -> bool {
if index < self.tabs.len() {
self.active_tab_index = Some(index);
true
} else {
false
}
}
pub fn tabs(&self) -> &[EditorTabState] {
&self.tabs
}
pub fn active_tab_index(&self) -> Option<usize> {
self.active_tab_index
}
pub fn tab_count(&self) -> usize {
self.tabs.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.tabs.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_app_state() {
let state = AppState::new();
assert_eq!(state.tab_count(), 0);
assert!(state.is_empty());
assert!(state.active_tab_index().is_none());
}
#[test]
fn test_open_new_tab() {
let mut state = AppState::new();
let id1 = state.open_new_tab("a.md");
assert_eq!(id1, 0);
assert_eq!(state.tab_count(), 1);
assert_eq!(state.active_tab_index(), Some(0));
let id2 = state.open_new_tab("b.md");
assert_eq!(id2, 1);
assert_eq!(state.tab_count(), 2);
let tabs = state.tabs();
assert_eq!(tabs[0].file_name, "a.md");
assert_eq!(tabs[1].file_name, "b.md");
}
#[test]
fn test_close_tab() {
let mut state = AppState::new();
state.open_new_tab("a.md");
state.open_new_tab("b.md");
state.open_new_tab("c.md");
assert_eq!(state.tab_count(), 3);
assert!(state.close_tab(1));
assert_eq!(state.tab_count(), 2);
let tabs = state.tabs();
assert_eq!(tabs[0].file_name, "a.md");
assert_eq!(tabs[1].file_name, "c.md");
}
#[test]
fn test_close_invalid_tab() {
let mut state = AppState::new();
state.open_new_tab("a.md");
assert!(!state.close_tab(10));
assert_eq!(state.tab_count(), 1);
}
#[test]
fn test_close_last_tab() {
let mut state = AppState::new();
state.open_new_tab("a.md");
assert!(state.close_tab(0));
assert_eq!(state.tab_count(), 0);
assert!(state.is_empty());
assert!(state.active_tab_index().is_none());
}
#[test]
fn test_active_tab() {
let mut state = AppState::new();
state.open_new_tab("a.md");
state.open_new_tab("b.md");
let active = state.active_tab();
assert!(active.is_some());
assert_eq!(active.unwrap().file_name, "a.md");
}
#[test]
fn test_active_tab_mut() {
let mut state = AppState::new();
state.open_new_tab("a.md");
if let Some(tab) = state.active_tab_mut() {
tab.mark_modified();
}
let active = state.active_tab();
assert!(active.unwrap().is_modified);
}
#[test]
fn test_set_active_tab() {
let mut state = AppState::new();
state.open_new_tab("a.md");
state.open_new_tab("b.md");
state.open_new_tab("c.md");
assert!(state.set_active_tab(2));
assert_eq!(state.active_tab_index(), Some(2));
assert_eq!(state.active_tab().unwrap().file_name, "c.md");
assert!(!state.set_active_tab(10));
assert_eq!(state.active_tab_index(), Some(2)); }
#[test]
fn test_close_active_tab() {
let mut state = AppState::new();
state.open_new_tab("a.md");
state.open_new_tab("b.md");
state.open_new_tab("c.md");
state.set_active_tab(1);
assert_eq!(state.active_tab().unwrap().file_name, "b.md");
state.close_tab(1);
assert_eq!(state.tab_count(), 2);
let active = state.active_tab();
assert!(active.is_some());
}
}