use crate::edit_control::{EditModificationInterface, EditModification};
use crate::cli::preview::{EditPreviewBuilder, PreviewResult};
use crossterm::{
cursor,
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
execute,
style::{Color, ResetColor, SetForegroundColor},
terminal::{self, ClearType, size},
};
use std::io::{self, stdout, Write};
use std::time::{Duration, Instant};
pub struct AdvancedPreviewSystem {
interface: EditModificationInterface,
editor_pane: EditorPane,
preview_pane: PreviewPane,
layout: SplitLayout,
active_pane: ActivePane,
auto_preview: bool,
preview_delay: Duration,
last_edit: Instant,
}
#[derive(Debug, Clone)]
pub struct EditorPane {
content: Vec<String>,
cursor_row: usize,
cursor_col: usize,
scroll_offset: usize,
syntax_highlighting: bool,
show_line_numbers: bool,
width: u16,
height: u16,
}
impl EditorPane {
pub fn content(&self) -> &Vec<String> {
&self.content
}
pub fn syntax_highlighting(&self) -> bool {
self.syntax_highlighting
}
pub fn show_line_numbers(&self) -> bool {
self.show_line_numbers
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
}
#[derive(Debug)]
pub struct PreviewPane {
preview_result: Option<PreviewResult>,
scroll_offset: usize,
auto_refresh: bool,
width: u16,
height: u16,
last_update: Instant,
}
impl PreviewPane {
pub fn auto_refresh(&self) -> bool {
self.auto_refresh
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SplitLayout {
Horizontal, Vertical, EditorOnly, PreviewOnly, }
#[derive(Debug, Clone, PartialEq)]
pub enum ActivePane {
Editor,
Preview,
}
impl AdvancedPreviewSystem {
pub fn new(interface: EditModificationInterface) -> Self {
let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
let editor_content: Vec<String> = interface
.modifiable_edit
.compute_final_code()
.lines()
.map(|s| s.to_string())
.collect();
let editor_pane = EditorPane {
content: editor_content,
cursor_row: 0,
cursor_col: 0,
scroll_offset: 0,
syntax_highlighting: true,
show_line_numbers: true,
width: terminal_width / 2,
height: terminal_height.saturating_sub(6), };
let preview_pane = PreviewPane {
preview_result: None,
scroll_offset: 0,
auto_refresh: true,
width: terminal_width / 2,
height: terminal_height.saturating_sub(6),
last_update: Instant::now(),
};
Self {
interface,
editor_pane,
preview_pane,
layout: SplitLayout::Horizontal,
active_pane: ActivePane::Editor,
auto_preview: true,
preview_delay: Duration::from_millis(500), last_edit: Instant::now(),
}
}
pub fn layout(&self) -> &SplitLayout {
&self.layout
}
pub fn active_pane(&self) -> &ActivePane {
&self.active_pane
}
pub fn auto_preview(&self) -> bool {
self.auto_preview
}
pub fn preview_delay(&self) -> Duration {
self.preview_delay
}
pub fn editor_pane(&self) -> &EditorPane {
&self.editor_pane
}
pub fn preview_pane(&self) -> &PreviewPane {
&self.preview_pane
}
pub fn interface(&self) -> &EditModificationInterface {
&self.interface
}
pub fn interface_mut(&mut self) -> &mut EditModificationInterface {
&mut self.interface
}
pub fn edit_with_live_preview(&mut self) -> io::Result<Option<EditModificationInterface>> {
terminal::enable_raw_mode()?;
self.update_preview();
let result = loop {
self.render()?;
if self.auto_preview && self.should_refresh_preview() {
self.update_preview();
}
if let Event::Key(key_event) = event::read()? {
match self.handle_key(key_event)? {
PreviewAction::Exit => {
break Ok(None);
}
PreviewAction::Save => {
break Ok(Some(self.interface.clone()));
}
PreviewAction::Continue => {}
}
}
};
terminal::disable_raw_mode()?;
result
}
fn render(&self) -> io::Result<()> {
execute!(
stdout(),
terminal::Clear(ClearType::All),
cursor::MoveTo(0, 0)
)?;
self.render_header()?;
match self.layout {
SplitLayout::Horizontal => self.render_horizontal_split()?,
SplitLayout::Vertical => self.render_vertical_split()?,
SplitLayout::EditorOnly => self.render_editor_only()?,
SplitLayout::PreviewOnly => self.render_preview_only()?,
}
self.render_footer()?;
self.position_cursor()?;
stdout().flush()
}
fn render_header(&self) -> io::Result<()> {
execute!(stdout(), SetForegroundColor(Color::Cyan))?;
println!("🔄 SOMA AdvancedPreviewSystem v1.0 - Issue #18 - Split-Pane Live Editor");
execute!(stdout(), ResetColor)?;
println!(
"📁 {} | Layout: {:?} | Active: {:?} | Auto-preview: {} | Changes: {}",
self.interface.modifiable_edit.base_edit.file,
self.layout,
self.active_pane,
if self.auto_preview { "ON" } else { "OFF" },
self.interface.modification_count()
);
println!(); Ok(())
}
fn render_horizontal_split(&self) -> io::Result<()> {
let (terminal_width, _) = size().unwrap_or((80, 24));
let split_pos = terminal_width / 2;
execute!(stdout(), SetForegroundColor(Color::Yellow))?;
print!("┌");
for _ in 0..split_pos.saturating_sub(2) {
print!("─");
}
print!("┬");
for _ in split_pos..terminal_width.saturating_sub(1) {
print!("─");
}
println!("┐");
print!("│");
execute!(stdout(), SetForegroundColor(Color::Green))?;
let editor_title = if self.active_pane == ActivePane::Editor { "► Editor" } else { " Editor" };
print!("{:^width$}", editor_title, width = split_pos.saturating_sub(2) as usize);
execute!(stdout(), SetForegroundColor(Color::Yellow))?;
print!("│");
execute!(stdout(), SetForegroundColor(Color::Blue))?;
let preview_title = if self.active_pane == ActivePane::Preview { "► Live Preview" } else { " Live Preview" };
print!("{:^width$}", preview_title, width = (terminal_width - split_pos - 1) as usize);
execute!(stdout(), SetForegroundColor(Color::Yellow))?;
println!("│");
print!("├");
for _ in 0..split_pos.saturating_sub(2) {
print!("─");
}
print!("┼");
for _ in split_pos..terminal_width.saturating_sub(1) {
print!("─");
}
println!("┤");
execute!(stdout(), ResetColor)?;
let content_height = self.editor_pane.height.min(self.preview_pane.height) as usize;
for row in 0..content_height {
print!("│");
self.render_editor_line(row, split_pos.saturating_sub(2) as usize)?;
print!("│");
self.render_preview_line(row, (terminal_width - split_pos - 1) as usize)?;
println!("│");
}
execute!(stdout(), SetForegroundColor(Color::Yellow))?;
print!("└");
for _ in 0..split_pos.saturating_sub(2) {
print!("─");
}
print!("┴");
for _ in split_pos..terminal_width.saturating_sub(1) {
print!("─");
}
println!("┘");
execute!(stdout(), ResetColor)?;
Ok(())
}
fn render_vertical_split(&self) -> io::Result<()> {
let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
let split_pos = terminal_height / 2;
execute!(stdout(), SetForegroundColor(Color::Green))?;
let editor_title = if self.active_pane == ActivePane::Editor { "► Editor" } else { " Editor" };
println!("┌─ {} {}", editor_title, "─".repeat(terminal_width.saturating_sub(12) as usize));
execute!(stdout(), ResetColor)?;
for row in 0..split_pos.saturating_sub(4) as usize {
self.render_editor_line(row, terminal_width as usize)?;
println!();
}
execute!(stdout(), SetForegroundColor(Color::Yellow))?;
println!("{}", "─".repeat(terminal_width as usize));
execute!(stdout(), ResetColor)?;
execute!(stdout(), SetForegroundColor(Color::Blue))?;
let preview_title = if self.active_pane == ActivePane::Preview { "► Live Preview" } else { " Live Preview" };
println!("└─ {} {}", preview_title, "─".repeat(terminal_width.saturating_sub(17) as usize));
execute!(stdout(), ResetColor)?;
for row in 0..(terminal_height - split_pos - 2) as usize {
self.render_preview_line(row, terminal_width as usize)?;
println!();
}
Ok(())
}
fn render_editor_only(&self) -> io::Result<()> {
let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
execute!(stdout(), SetForegroundColor(Color::Green))?;
println!("┌─ ► Full Editor Mode {}", "─".repeat(terminal_width.saturating_sub(21) as usize));
execute!(stdout(), ResetColor)?;
for row in 0..terminal_height.saturating_sub(6) as usize {
self.render_editor_line(row, terminal_width as usize)?;
println!();
}
Ok(())
}
fn render_preview_only(&self) -> io::Result<()> {
let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
execute!(stdout(), SetForegroundColor(Color::Blue))?;
println!("┌─ ► Full Preview Mode {}", "─".repeat(terminal_width.saturating_sub(22) as usize));
execute!(stdout(), ResetColor)?;
for row in 0..terminal_height.saturating_sub(6) as usize {
self.render_preview_line(row, terminal_width as usize)?;
println!();
}
Ok(())
}
fn render_editor_line(&self, row: usize, width: usize) -> io::Result<()> {
let content_row = row + self.editor_pane.scroll_offset;
if content_row < self.editor_pane.content.len() {
let line = &self.editor_pane.content[content_row];
let cursor_marker = if content_row == self.editor_pane.cursor_row { ">" } else { " " };
let display_text = if self.editor_pane.show_line_numbers {
format!("{:3}:{} {}", content_row + 1, cursor_marker, line)
} else {
format!("{} {}", cursor_marker, line)
};
if self.editor_pane.syntax_highlighting && self.active_pane == ActivePane::Editor {
self.render_syntax_highlighted(&display_text, width)?;
} else {
print!("{:width$}", display_text.chars().take(width).collect::<String>(), width = width);
}
} else {
print!("{:width$}", "~", width = width);
}
Ok(())
}
fn render_preview_line(&self, row: usize, width: usize) -> io::Result<()> {
if let Some(ref preview) = self.preview_pane.preview_result {
let preview_text = self.get_preview_line(preview, row);
print!("{:width$}", preview_text.chars().take(width).collect::<String>(), width = width);
} else {
execute!(stdout(), SetForegroundColor(Color::DarkGrey))?;
print!("{:width$}", "Preview updating...", width = width);
execute!(stdout(), ResetColor)?;
}
Ok(())
}
fn get_preview_line(&self, preview: &PreviewResult, row: usize) -> String {
let content_row = row + self.preview_pane.scroll_offset;
let mut line_index = 0;
for section in &preview.sections {
let section_lines: Vec<&str> = section.content.lines().collect();
if content_row == line_index {
return format!("╔═ {} ═╗", section.title);
}
line_index += 1;
for (i, line) in section_lines.iter().enumerate() {
if content_row == line_index {
return if i == section_lines.len() - 1 {
format!("╚ {} ╝", line)
} else {
format!("║ {} ║", line)
};
}
line_index += 1;
}
if content_row == line_index {
return "║".to_string();
}
line_index += 1;
}
"".to_string()
}
fn render_syntax_highlighted(&self, text: &str, width: usize) -> io::Result<()> {
let mut in_string = false;
let mut in_comment = false;
for (i, ch) in text.chars().enumerate() {
if i >= width { break; }
if in_comment {
execute!(stdout(), SetForegroundColor(Color::DarkGreen))?;
print!("{}", ch);
} else if in_string {
execute!(stdout(), SetForegroundColor(Color::Yellow))?;
print!("{}", ch);
if ch == '"' {
in_string = false;
execute!(stdout(), ResetColor)?;
}
} else {
match ch {
'/' if text.chars().nth(i + 1) == Some('/') => {
in_comment = true;
execute!(stdout(), SetForegroundColor(Color::DarkGreen))?;
print!("{}", ch);
}
'"' => {
in_string = true;
execute!(stdout(), SetForegroundColor(Color::Yellow))?;
print!("{}", ch);
}
_ => {
if text[..i].ends_with("fn ") || text[..i].ends_with("let ") || text[..i].ends_with("pub ") {
execute!(stdout(), SetForegroundColor(Color::Blue))?;
} else {
execute!(stdout(), ResetColor)?;
}
print!("{}", ch);
}
}
}
}
for _ in text.chars().count()..width {
print!(" ");
}
execute!(stdout(), ResetColor)?;
Ok(())
}
fn render_footer(&self) -> io::Result<()> {
execute!(stdout(), SetForegroundColor(Color::Cyan))?;
println!("─────────────────────────────────────────────────────────────────────────");
execute!(stdout(), ResetColor)?;
println!(
"Cursor: {}:{} | Undo: {} | Redo: {} | Last Update: {:.1}s ago | Tab: Switch Pane",
self.editor_pane.cursor_row + 1,
self.editor_pane.cursor_col + 1,
if self.interface.can_undo() { "✓" } else { "✗" },
if self.interface.can_redo() { "✓" } else { "✗" },
self.preview_pane.last_update.elapsed().as_secs_f32()
);
println!("Ctrl+S: Save | Ctrl+Q: Quit | Ctrl+L: Layout | Ctrl+A: Auto-preview | Ctrl+R: Refresh");
Ok(())
}
fn position_cursor(&self) -> io::Result<()> {
if self.active_pane == ActivePane::Editor {
let header_offset = 6; let display_row = header_offset + self.editor_pane.cursor_row - self.editor_pane.scroll_offset;
let display_col = match self.layout {
SplitLayout::Horizontal => {
let line_num_offset = if self.editor_pane.show_line_numbers { 6 } else { 2 };
1 + line_num_offset + self.editor_pane.cursor_col
}
_ => {
let line_num_offset = if self.editor_pane.show_line_numbers { 6 } else { 2 };
line_num_offset + self.editor_pane.cursor_col
}
};
execute!(
stdout(),
cursor::MoveTo(display_col as u16, display_row as u16)
)?;
}
Ok(())
}
fn handle_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
match (key.code, key.modifiers) {
(KeyCode::Char('q'), KeyModifiers::CONTROL) => Ok(PreviewAction::Exit),
(KeyCode::Char('s'), KeyModifiers::CONTROL) => {
self.save_editor_changes();
Ok(PreviewAction::Save)
},
(KeyCode::Char('l'), KeyModifiers::CONTROL) => {
self.cycle_layout();
Ok(PreviewAction::Continue)
},
(KeyCode::Tab, _) => {
self.switch_active_pane();
Ok(PreviewAction::Continue)
},
(KeyCode::Char('a'), KeyModifiers::CONTROL) => {
self.auto_preview = !self.auto_preview;
Ok(PreviewAction::Continue)
},
(KeyCode::Char('r'), KeyModifiers::CONTROL) => {
self.update_preview();
Ok(PreviewAction::Continue)
},
(KeyCode::Char('z'), KeyModifiers::CONTROL) => {
if self.interface.undo() {
self.update_editor_from_interface();
if self.auto_preview {
self.update_preview();
}
}
Ok(PreviewAction::Continue)
},
(KeyCode::Char('y'), KeyModifiers::CONTROL) => {
if self.interface.redo() {
self.update_editor_from_interface();
if self.auto_preview {
self.update_preview();
}
}
Ok(PreviewAction::Continue)
},
_ if self.active_pane == ActivePane::Editor => {
self.handle_editor_key(key)
},
_ if self.active_pane == ActivePane::Preview => {
self.handle_preview_key(key)
},
_ => Ok(PreviewAction::Continue),
}
}
fn handle_editor_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
match key.code {
KeyCode::Up => {
if self.editor_pane.cursor_row > 0 {
self.editor_pane.cursor_row -= 1;
self.adjust_editor_scroll();
self.adjust_cursor_bounds();
}
}
KeyCode::Down => {
if self.editor_pane.cursor_row < self.editor_pane.content.len().saturating_sub(1) {
self.editor_pane.cursor_row += 1;
self.adjust_editor_scroll();
self.adjust_cursor_bounds();
}
}
KeyCode::Left => {
if self.editor_pane.cursor_col > 0 {
self.editor_pane.cursor_col -= 1;
} else if self.editor_pane.cursor_row > 0 {
self.editor_pane.cursor_row -= 1;
self.editor_pane.cursor_col = self.editor_pane.content
.get(self.editor_pane.cursor_row)
.map(|s| s.len())
.unwrap_or(0);
self.adjust_editor_scroll();
}
}
KeyCode::Right => {
let line_len = self.editor_pane.content
.get(self.editor_pane.cursor_row)
.map(|s| s.len())
.unwrap_or(0);
if self.editor_pane.cursor_col < line_len {
self.editor_pane.cursor_col += 1;
} else if self.editor_pane.cursor_row < self.editor_pane.content.len().saturating_sub(1) {
self.editor_pane.cursor_row += 1;
self.editor_pane.cursor_col = 0;
self.adjust_editor_scroll();
}
}
KeyCode::Home => {
self.editor_pane.cursor_col = 0;
}
KeyCode::End => {
self.editor_pane.cursor_col = self.editor_pane.content
.get(self.editor_pane.cursor_row)
.map(|s| s.len())
.unwrap_or(0);
}
KeyCode::Enter => {
self.insert_newline();
self.mark_editor_changed();
}
KeyCode::Char(c) => {
self.insert_char(c);
self.mark_editor_changed();
}
KeyCode::Backspace => {
self.delete_char();
self.mark_editor_changed();
}
KeyCode::Delete => {
self.delete_char_forward();
self.mark_editor_changed();
}
_ => {}
}
Ok(PreviewAction::Continue)
}
fn handle_preview_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
match key.code {
KeyCode::Up => {
if self.preview_pane.scroll_offset > 0 {
self.preview_pane.scroll_offset -= 1;
}
}
KeyCode::Down => {
self.preview_pane.scroll_offset += 1;
}
KeyCode::PageUp => {
self.preview_pane.scroll_offset = self.preview_pane.scroll_offset.saturating_sub(10);
}
KeyCode::PageDown => {
self.preview_pane.scroll_offset += 10;
}
KeyCode::Home => {
self.preview_pane.scroll_offset = 0;
}
_ => {}
}
Ok(PreviewAction::Continue)
}
fn cycle_layout(&mut self) {
self.layout = match self.layout {
SplitLayout::Horizontal => SplitLayout::Vertical,
SplitLayout::Vertical => SplitLayout::EditorOnly,
SplitLayout::EditorOnly => SplitLayout::PreviewOnly,
SplitLayout::PreviewOnly => SplitLayout::Horizontal,
};
self.update_pane_dimensions();
}
fn switch_active_pane(&mut self) {
self.active_pane = match self.active_pane {
ActivePane::Editor => ActivePane::Preview,
ActivePane::Preview => ActivePane::Editor,
};
}
fn update_pane_dimensions(&mut self) {
let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
let content_height = terminal_height.saturating_sub(6);
match self.layout {
SplitLayout::Horizontal => {
self.editor_pane.width = terminal_width / 2;
self.editor_pane.height = content_height;
self.preview_pane.width = terminal_width / 2;
self.preview_pane.height = content_height;
}
SplitLayout::Vertical => {
self.editor_pane.width = terminal_width;
self.editor_pane.height = content_height / 2;
self.preview_pane.width = terminal_width;
self.preview_pane.height = content_height / 2;
}
SplitLayout::EditorOnly => {
self.editor_pane.width = terminal_width;
self.editor_pane.height = content_height;
}
SplitLayout::PreviewOnly => {
self.preview_pane.width = terminal_width;
self.preview_pane.height = content_height;
}
}
}
fn should_refresh_preview(&self) -> bool {
self.last_edit.elapsed() >= self.preview_delay
}
pub fn update_preview(&mut self) {
let preview_builder = EditPreviewBuilder::new()
.with_diff(true)
.with_risk_assessment(true)
.with_syntax_highlighting(true);
let preview = preview_builder.build();
self.preview_pane.preview_result = Some(preview.generate_preview(&self.interface.modifiable_edit));
self.preview_pane.last_update = Instant::now();
}
fn mark_editor_changed(&mut self) {
self.last_edit = Instant::now();
}
fn save_editor_changes(&mut self) {
let new_content = self.editor_pane.content.join("\n");
let old_content = self.interface.modifiable_edit.compute_final_code();
if new_content != old_content {
self.interface.add_modification(
EditModification::CodeChange {
line: 0,
old: old_content,
new: new_content,
},
"Live editor changes".to_string(),
);
self.update_preview();
}
}
fn update_editor_from_interface(&mut self) {
let new_content = self.interface.modifiable_edit.compute_final_code();
self.editor_pane.content = new_content.lines().map(|s| s.to_string()).collect();
}
fn adjust_cursor_bounds(&mut self) {
if let Some(line) = self.editor_pane.content.get(self.editor_pane.cursor_row) {
if self.editor_pane.cursor_col > line.len() {
self.editor_pane.cursor_col = line.len();
}
}
}
fn adjust_editor_scroll(&mut self) {
let visible_lines = self.editor_pane.height as usize;
if self.editor_pane.cursor_row < self.editor_pane.scroll_offset {
self.editor_pane.scroll_offset = self.editor_pane.cursor_row;
} else if self.editor_pane.cursor_row >= self.editor_pane.scroll_offset + visible_lines {
self.editor_pane.scroll_offset = self.editor_pane.cursor_row - visible_lines + 1;
}
}
fn insert_char(&mut self, c: char) {
if self.editor_pane.cursor_row < self.editor_pane.content.len() {
self.editor_pane.content[self.editor_pane.cursor_row].insert(self.editor_pane.cursor_col, c);
self.editor_pane.cursor_col += 1;
}
}
fn insert_newline(&mut self) {
if self.editor_pane.cursor_row < self.editor_pane.content.len() {
let current_line = self.editor_pane.content[self.editor_pane.cursor_row].clone();
let (left, right) = current_line.split_at(self.editor_pane.cursor_col);
self.editor_pane.content[self.editor_pane.cursor_row] = left.to_string();
self.editor_pane.content.insert(self.editor_pane.cursor_row + 1, right.to_string());
self.editor_pane.cursor_row += 1;
self.editor_pane.cursor_col = 0;
self.adjust_editor_scroll();
}
}
fn delete_char(&mut self) {
if self.editor_pane.cursor_col > 0 && self.editor_pane.cursor_row < self.editor_pane.content.len() {
self.editor_pane.content[self.editor_pane.cursor_row].remove(self.editor_pane.cursor_col - 1);
self.editor_pane.cursor_col -= 1;
} else if self.editor_pane.cursor_row > 0 {
let current_line = self.editor_pane.content.remove(self.editor_pane.cursor_row);
self.editor_pane.cursor_row -= 1;
self.editor_pane.cursor_col = self.editor_pane.content[self.editor_pane.cursor_row].len();
self.editor_pane.content[self.editor_pane.cursor_row].push_str(¤t_line);
self.adjust_editor_scroll();
}
}
fn delete_char_forward(&mut self) {
if self.editor_pane.cursor_row < self.editor_pane.content.len() {
let line_len = self.editor_pane.content[self.editor_pane.cursor_row].len();
if self.editor_pane.cursor_col < line_len {
self.editor_pane.content[self.editor_pane.cursor_row].remove(self.editor_pane.cursor_col);
} else if self.editor_pane.cursor_row < self.editor_pane.content.len() - 1 {
let next_line = self.editor_pane.content.remove(self.editor_pane.cursor_row + 1);
self.editor_pane.content[self.editor_pane.cursor_row].push_str(&next_line);
}
}
}
}
#[derive(Debug)]
enum PreviewAction {
Continue,
Save,
Exit,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agents::gpt4_agent::ProposedEdit;
use crate::edit_control::ModifiableEdit;
fn create_test_interface() -> EditModificationInterface {
let proposed = ProposedEdit {
file: "test.rs".to_string(),
line_range: (10, 15),
new_code: "fn test() {\n println!(\"Hello, World!\");\n return 42;\n}".to_string(),
reason: "Test function for AdvancedPreviewSystem".to_string(),
confidence: 0.9,
};
let modifiable = ModifiableEdit::from_proposed_edit(proposed);
EditModificationInterface::new(modifiable)
}
#[test]
fn test_advanced_preview_system_creation() {
let interface = create_test_interface();
let preview_system = AdvancedPreviewSystem::new(interface);
assert_eq!(preview_system.layout, SplitLayout::Horizontal);
assert_eq!(preview_system.active_pane, ActivePane::Editor);
assert!(preview_system.auto_preview);
assert_eq!(preview_system.editor_pane.content.len(), 4); assert_eq!(preview_system.editor_pane.cursor_row, 0);
assert_eq!(preview_system.editor_pane.cursor_col, 0);
}
#[test]
fn test_layout_cycling() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
assert_eq!(preview_system.layout, SplitLayout::Horizontal);
preview_system.cycle_layout();
assert_eq!(preview_system.layout, SplitLayout::Vertical);
preview_system.cycle_layout();
assert_eq!(preview_system.layout, SplitLayout::EditorOnly);
preview_system.cycle_layout();
assert_eq!(preview_system.layout, SplitLayout::PreviewOnly);
preview_system.cycle_layout();
assert_eq!(preview_system.layout, SplitLayout::Horizontal);
}
#[test]
fn test_pane_switching() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
assert_eq!(preview_system.active_pane, ActivePane::Editor);
preview_system.switch_active_pane();
assert_eq!(preview_system.active_pane, ActivePane::Preview);
preview_system.switch_active_pane();
assert_eq!(preview_system.active_pane, ActivePane::Editor);
}
#[test]
fn test_editor_text_insertion() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
let initial_line = preview_system.editor_pane.content[0].clone();
preview_system.insert_char('X');
assert_eq!(preview_system.editor_pane.cursor_col, 1);
assert!(preview_system.editor_pane.content[0].starts_with('X'));
assert_ne!(preview_system.editor_pane.content[0], initial_line);
}
#[test]
fn test_editor_newline_insertion() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
let initial_lines = preview_system.editor_pane.content.len();
preview_system.insert_newline();
assert_eq!(preview_system.editor_pane.content.len(), initial_lines + 1);
assert_eq!(preview_system.editor_pane.cursor_row, 1);
assert_eq!(preview_system.editor_pane.cursor_col, 0);
}
#[test]
fn test_cursor_movement() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
let initial_col = preview_system.editor_pane.cursor_col;
preview_system.editor_pane.cursor_col = 5;
preview_system.adjust_cursor_bounds();
assert!(preview_system.editor_pane.cursor_col >= initial_col);
preview_system.editor_pane.cursor_row = 1;
preview_system.adjust_cursor_bounds();
assert!(preview_system.editor_pane.cursor_row <= preview_system.editor_pane.content.len());
}
#[test]
fn test_scroll_adjustment() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
preview_system.editor_pane.cursor_row = 50;
preview_system.editor_pane.height = 20; preview_system.adjust_editor_scroll();
assert!(preview_system.editor_pane.scroll_offset > 0);
assert!(preview_system.editor_pane.cursor_row >= preview_system.editor_pane.scroll_offset);
}
#[test]
fn test_auto_preview_timing() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
assert!(!preview_system.should_refresh_preview());
preview_system.preview_delay = Duration::from_nanos(1);
std::thread::sleep(Duration::from_millis(1));
assert!(preview_system.should_refresh_preview());
}
#[test]
fn test_preview_line_extraction() {
let interface = create_test_interface();
let mut preview_system = AdvancedPreviewSystem::new(interface);
preview_system.update_preview();
if let Some(ref preview) = preview_system.preview_pane.preview_result {
let line = preview_system.get_preview_line(preview, 0);
assert!(!line.is_empty());
assert!(line.contains("╔═") || line.contains("║") || line.is_empty());
}
}
}