1use crate::edit_control::{EditModificationInterface, EditModification};
5use crate::cli::preview::{EditPreviewBuilder, PreviewResult};
6use crossterm::{
7 cursor,
8 event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
9 execute,
10 style::{Color, ResetColor, SetForegroundColor},
11 terminal::{self, ClearType, size},
12};
13use std::io::{self, stdout, Write};
14use std::time::{Duration, Instant};
15
16pub struct AdvancedPreviewSystem {
19 interface: EditModificationInterface,
20 editor_pane: EditorPane,
21 preview_pane: PreviewPane,
22 layout: SplitLayout,
23 active_pane: ActivePane,
24 auto_preview: bool,
25 preview_delay: Duration,
26 last_edit: Instant,
27}
28
29#[derive(Debug, Clone)]
30pub struct EditorPane {
31 content: Vec<String>,
32 cursor_row: usize,
33 cursor_col: usize,
34 scroll_offset: usize,
35 syntax_highlighting: bool,
36 show_line_numbers: bool,
37 width: u16,
38 height: u16,
39}
40
41impl EditorPane {
42 pub fn content(&self) -> &Vec<String> {
43 &self.content
44 }
45
46 pub fn syntax_highlighting(&self) -> bool {
47 self.syntax_highlighting
48 }
49
50 pub fn show_line_numbers(&self) -> bool {
51 self.show_line_numbers
52 }
53
54 pub fn width(&self) -> u16 {
55 self.width
56 }
57
58 pub fn height(&self) -> u16 {
59 self.height
60 }
61}
62
63#[derive(Debug)]
64pub struct PreviewPane {
65 preview_result: Option<PreviewResult>,
66 scroll_offset: usize,
67 auto_refresh: bool,
68 width: u16,
69 height: u16,
70 last_update: Instant,
71}
72
73impl PreviewPane {
74 pub fn auto_refresh(&self) -> bool {
75 self.auto_refresh
76 }
77
78 pub fn width(&self) -> u16 {
79 self.width
80 }
81
82 pub fn height(&self) -> u16 {
83 self.height
84 }
85}
86
87#[derive(Debug, Clone, PartialEq)]
88pub enum SplitLayout {
89 Horizontal, Vertical, EditorOnly, PreviewOnly, }
94
95#[derive(Debug, Clone, PartialEq)]
96pub enum ActivePane {
97 Editor,
98 Preview,
99}
100
101impl AdvancedPreviewSystem {
102 pub fn new(interface: EditModificationInterface) -> Self {
104 let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
105
106 let editor_content: Vec<String> = interface
107 .modifiable_edit
108 .compute_final_code()
109 .lines()
110 .map(|s| s.to_string())
111 .collect();
112
113 let editor_pane = EditorPane {
114 content: editor_content,
115 cursor_row: 0,
116 cursor_col: 0,
117 scroll_offset: 0,
118 syntax_highlighting: true,
119 show_line_numbers: true,
120 width: terminal_width / 2,
121 height: terminal_height.saturating_sub(6), };
123
124 let preview_pane = PreviewPane {
125 preview_result: None,
126 scroll_offset: 0,
127 auto_refresh: true,
128 width: terminal_width / 2,
129 height: terminal_height.saturating_sub(6),
130 last_update: Instant::now(),
131 };
132
133 Self {
134 interface,
135 editor_pane,
136 preview_pane,
137 layout: SplitLayout::Horizontal,
138 active_pane: ActivePane::Editor,
139 auto_preview: true,
140 preview_delay: Duration::from_millis(500), last_edit: Instant::now(),
142 }
143 }
144
145 pub fn layout(&self) -> &SplitLayout {
147 &self.layout
148 }
149
150 pub fn active_pane(&self) -> &ActivePane {
151 &self.active_pane
152 }
153
154 pub fn auto_preview(&self) -> bool {
155 self.auto_preview
156 }
157
158 pub fn preview_delay(&self) -> Duration {
159 self.preview_delay
160 }
161
162 pub fn editor_pane(&self) -> &EditorPane {
163 &self.editor_pane
164 }
165
166 pub fn preview_pane(&self) -> &PreviewPane {
167 &self.preview_pane
168 }
169
170 pub fn interface(&self) -> &EditModificationInterface {
171 &self.interface
172 }
173
174 pub fn interface_mut(&mut self) -> &mut EditModificationInterface {
175 &mut self.interface
176 }
177
178 pub fn edit_with_live_preview(&mut self) -> io::Result<Option<EditModificationInterface>> {
180 terminal::enable_raw_mode()?;
181
182 self.update_preview();
184
185 let result = loop {
186 self.render()?;
187
188 if self.auto_preview && self.should_refresh_preview() {
190 self.update_preview();
191 }
192
193 if let Event::Key(key_event) = event::read()? {
194 match self.handle_key(key_event)? {
195 PreviewAction::Exit => {
196 break Ok(None);
197 }
198 PreviewAction::Save => {
199 break Ok(Some(self.interface.clone()));
200 }
201 PreviewAction::Continue => {}
202 }
203 }
204 };
205
206 terminal::disable_raw_mode()?;
207 result
208 }
209
210 fn render(&self) -> io::Result<()> {
212 execute!(
213 stdout(),
214 terminal::Clear(ClearType::All),
215 cursor::MoveTo(0, 0)
216 )?;
217
218 self.render_header()?;
220
221 match self.layout {
222 SplitLayout::Horizontal => self.render_horizontal_split()?,
223 SplitLayout::Vertical => self.render_vertical_split()?,
224 SplitLayout::EditorOnly => self.render_editor_only()?,
225 SplitLayout::PreviewOnly => self.render_preview_only()?,
226 }
227
228 self.render_footer()?;
230
231 self.position_cursor()?;
233
234 stdout().flush()
235 }
236
237 fn render_header(&self) -> io::Result<()> {
238 execute!(stdout(), SetForegroundColor(Color::Cyan))?;
239 println!("🔄 SOMA AdvancedPreviewSystem v1.0 - Issue #18 - Split-Pane Live Editor");
240 execute!(stdout(), ResetColor)?;
241
242 println!(
243 "📁 {} | Layout: {:?} | Active: {:?} | Auto-preview: {} | Changes: {}",
244 self.interface.modifiable_edit.base_edit.file,
245 self.layout,
246 self.active_pane,
247 if self.auto_preview { "ON" } else { "OFF" },
248 self.interface.modification_count()
249 );
250
251 println!(); Ok(())
253 }
254
255 fn render_horizontal_split(&self) -> io::Result<()> {
256 let (terminal_width, _) = size().unwrap_or((80, 24));
257 let split_pos = terminal_width / 2;
258
259 execute!(stdout(), SetForegroundColor(Color::Yellow))?;
261 print!("┌");
262 for _ in 0..split_pos.saturating_sub(2) {
263 print!("─");
264 }
265 print!("┬");
266 for _ in split_pos..terminal_width.saturating_sub(1) {
267 print!("─");
268 }
269 println!("┐");
270
271 print!("│");
272 execute!(stdout(), SetForegroundColor(Color::Green))?;
273 let editor_title = if self.active_pane == ActivePane::Editor { "► Editor" } else { " Editor" };
274 print!("{:^width$}", editor_title, width = split_pos.saturating_sub(2) as usize);
275 execute!(stdout(), SetForegroundColor(Color::Yellow))?;
276 print!("│");
277 execute!(stdout(), SetForegroundColor(Color::Blue))?;
278 let preview_title = if self.active_pane == ActivePane::Preview { "► Live Preview" } else { " Live Preview" };
279 print!("{:^width$}", preview_title, width = (terminal_width - split_pos - 1) as usize);
280 execute!(stdout(), SetForegroundColor(Color::Yellow))?;
281 println!("│");
282
283 print!("├");
284 for _ in 0..split_pos.saturating_sub(2) {
285 print!("─");
286 }
287 print!("┼");
288 for _ in split_pos..terminal_width.saturating_sub(1) {
289 print!("─");
290 }
291 println!("┤");
292 execute!(stdout(), ResetColor)?;
293
294 let content_height = self.editor_pane.height.min(self.preview_pane.height) as usize;
296 for row in 0..content_height {
297 print!("│");
299 self.render_editor_line(row, split_pos.saturating_sub(2) as usize)?;
300 print!("│");
301
302 self.render_preview_line(row, (terminal_width - split_pos - 1) as usize)?;
304 println!("│");
305 }
306
307 execute!(stdout(), SetForegroundColor(Color::Yellow))?;
309 print!("└");
310 for _ in 0..split_pos.saturating_sub(2) {
311 print!("─");
312 }
313 print!("┴");
314 for _ in split_pos..terminal_width.saturating_sub(1) {
315 print!("─");
316 }
317 println!("┘");
318 execute!(stdout(), ResetColor)?;
319
320 Ok(())
321 }
322
323 fn render_vertical_split(&self) -> io::Result<()> {
324 let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
325 let split_pos = terminal_height / 2;
326
327 execute!(stdout(), SetForegroundColor(Color::Green))?;
329 let editor_title = if self.active_pane == ActivePane::Editor { "► Editor" } else { " Editor" };
330 println!("┌─ {} {}", editor_title, "─".repeat(terminal_width.saturating_sub(12) as usize));
331 execute!(stdout(), ResetColor)?;
332
333 for row in 0..split_pos.saturating_sub(4) as usize {
334 self.render_editor_line(row, terminal_width as usize)?;
335 println!();
336 }
337
338 execute!(stdout(), SetForegroundColor(Color::Yellow))?;
340 println!("{}", "─".repeat(terminal_width as usize));
341 execute!(stdout(), ResetColor)?;
342
343 execute!(stdout(), SetForegroundColor(Color::Blue))?;
345 let preview_title = if self.active_pane == ActivePane::Preview { "► Live Preview" } else { " Live Preview" };
346 println!("└─ {} {}", preview_title, "─".repeat(terminal_width.saturating_sub(17) as usize));
347 execute!(stdout(), ResetColor)?;
348
349 for row in 0..(terminal_height - split_pos - 2) as usize {
350 self.render_preview_line(row, terminal_width as usize)?;
351 println!();
352 }
353
354 Ok(())
355 }
356
357 fn render_editor_only(&self) -> io::Result<()> {
358 let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
359
360 execute!(stdout(), SetForegroundColor(Color::Green))?;
361 println!("┌─ ► Full Editor Mode {}", "─".repeat(terminal_width.saturating_sub(21) as usize));
362 execute!(stdout(), ResetColor)?;
363
364 for row in 0..terminal_height.saturating_sub(6) as usize {
365 self.render_editor_line(row, terminal_width as usize)?;
366 println!();
367 }
368
369 Ok(())
370 }
371
372 fn render_preview_only(&self) -> io::Result<()> {
373 let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
374
375 execute!(stdout(), SetForegroundColor(Color::Blue))?;
376 println!("┌─ ► Full Preview Mode {}", "─".repeat(terminal_width.saturating_sub(22) as usize));
377 execute!(stdout(), ResetColor)?;
378
379 for row in 0..terminal_height.saturating_sub(6) as usize {
380 self.render_preview_line(row, terminal_width as usize)?;
381 println!();
382 }
383
384 Ok(())
385 }
386
387 fn render_editor_line(&self, row: usize, width: usize) -> io::Result<()> {
388 let content_row = row + self.editor_pane.scroll_offset;
389
390 if content_row < self.editor_pane.content.len() {
391 let line = &self.editor_pane.content[content_row];
392 let cursor_marker = if content_row == self.editor_pane.cursor_row { ">" } else { " " };
393
394 let display_text = if self.editor_pane.show_line_numbers {
395 format!("{:3}:{} {}", content_row + 1, cursor_marker, line)
396 } else {
397 format!("{} {}", cursor_marker, line)
398 };
399
400 if self.editor_pane.syntax_highlighting && self.active_pane == ActivePane::Editor {
401 self.render_syntax_highlighted(&display_text, width)?;
402 } else {
403 print!("{:width$}", display_text.chars().take(width).collect::<String>(), width = width);
404 }
405 } else {
406 print!("{:width$}", "~", width = width);
407 }
408
409 Ok(())
410 }
411
412 fn render_preview_line(&self, row: usize, width: usize) -> io::Result<()> {
413 if let Some(ref preview) = self.preview_pane.preview_result {
414 let preview_text = self.get_preview_line(preview, row);
415 print!("{:width$}", preview_text.chars().take(width).collect::<String>(), width = width);
416 } else {
417 execute!(stdout(), SetForegroundColor(Color::DarkGrey))?;
418 print!("{:width$}", "Preview updating...", width = width);
419 execute!(stdout(), ResetColor)?;
420 }
421
422 Ok(())
423 }
424
425 fn get_preview_line(&self, preview: &PreviewResult, row: usize) -> String {
426 let content_row = row + self.preview_pane.scroll_offset;
427 let mut line_index = 0;
428
429 for section in &preview.sections {
430 let section_lines: Vec<&str> = section.content.lines().collect();
431
432 if content_row == line_index {
433 return format!("╔═ {} ═╗", section.title);
434 }
435 line_index += 1;
436
437 for (i, line) in section_lines.iter().enumerate() {
438 if content_row == line_index {
439 return if i == section_lines.len() - 1 {
440 format!("╚ {} ╝", line)
441 } else {
442 format!("║ {} ║", line)
443 };
444 }
445 line_index += 1;
446 }
447
448 if content_row == line_index {
449 return "║".to_string();
450 }
451 line_index += 1;
452 }
453
454 "".to_string()
455 }
456
457 fn render_syntax_highlighted(&self, text: &str, width: usize) -> io::Result<()> {
458 let mut in_string = false;
460 let mut in_comment = false;
461
462 for (i, ch) in text.chars().enumerate() {
463 if i >= width { break; }
464
465 if in_comment {
466 execute!(stdout(), SetForegroundColor(Color::DarkGreen))?;
467 print!("{}", ch);
468 } else if in_string {
469 execute!(stdout(), SetForegroundColor(Color::Yellow))?;
470 print!("{}", ch);
471 if ch == '"' {
472 in_string = false;
473 execute!(stdout(), ResetColor)?;
474 }
475 } else {
476 match ch {
477 '/' if text.chars().nth(i + 1) == Some('/') => {
478 in_comment = true;
479 execute!(stdout(), SetForegroundColor(Color::DarkGreen))?;
480 print!("{}", ch);
481 }
482 '"' => {
483 in_string = true;
484 execute!(stdout(), SetForegroundColor(Color::Yellow))?;
485 print!("{}", ch);
486 }
487 _ => {
488 if text[..i].ends_with("fn ") || text[..i].ends_with("let ") || text[..i].ends_with("pub ") {
489 execute!(stdout(), SetForegroundColor(Color::Blue))?;
490 } else {
491 execute!(stdout(), ResetColor)?;
492 }
493 print!("{}", ch);
494 }
495 }
496 }
497 }
498
499 for _ in text.chars().count()..width {
501 print!(" ");
502 }
503
504 execute!(stdout(), ResetColor)?;
505 Ok(())
506 }
507
508 fn render_footer(&self) -> io::Result<()> {
509 execute!(stdout(), SetForegroundColor(Color::Cyan))?;
510 println!("─────────────────────────────────────────────────────────────────────────");
511 execute!(stdout(), ResetColor)?;
512
513 println!(
514 "Cursor: {}:{} | Undo: {} | Redo: {} | Last Update: {:.1}s ago | Tab: Switch Pane",
515 self.editor_pane.cursor_row + 1,
516 self.editor_pane.cursor_col + 1,
517 if self.interface.can_undo() { "✓" } else { "✗" },
518 if self.interface.can_redo() { "✓" } else { "✗" },
519 self.preview_pane.last_update.elapsed().as_secs_f32()
520 );
521
522 println!("Ctrl+S: Save | Ctrl+Q: Quit | Ctrl+L: Layout | Ctrl+A: Auto-preview | Ctrl+R: Refresh");
523
524 Ok(())
525 }
526
527 fn position_cursor(&self) -> io::Result<()> {
528 if self.active_pane == ActivePane::Editor {
529 let header_offset = 6; let display_row = header_offset + self.editor_pane.cursor_row - self.editor_pane.scroll_offset;
531 let display_col = match self.layout {
532 SplitLayout::Horizontal => {
533 let line_num_offset = if self.editor_pane.show_line_numbers { 6 } else { 2 };
534 1 + line_num_offset + self.editor_pane.cursor_col
535 }
536 _ => {
537 let line_num_offset = if self.editor_pane.show_line_numbers { 6 } else { 2 };
538 line_num_offset + self.editor_pane.cursor_col
539 }
540 };
541
542 execute!(
543 stdout(),
544 cursor::MoveTo(display_col as u16, display_row as u16)
545 )?;
546 }
547
548 Ok(())
549 }
550
551 fn handle_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
553 match (key.code, key.modifiers) {
554 (KeyCode::Char('q'), KeyModifiers::CONTROL) => Ok(PreviewAction::Exit),
556 (KeyCode::Char('s'), KeyModifiers::CONTROL) => {
557 self.save_editor_changes();
558 Ok(PreviewAction::Save)
559 },
560
561 (KeyCode::Char('l'), KeyModifiers::CONTROL) => {
563 self.cycle_layout();
564 Ok(PreviewAction::Continue)
565 },
566 (KeyCode::Tab, _) => {
567 self.switch_active_pane();
568 Ok(PreviewAction::Continue)
569 },
570
571 (KeyCode::Char('a'), KeyModifiers::CONTROL) => {
573 self.auto_preview = !self.auto_preview;
574 Ok(PreviewAction::Continue)
575 },
576 (KeyCode::Char('r'), KeyModifiers::CONTROL) => {
577 self.update_preview();
578 Ok(PreviewAction::Continue)
579 },
580
581 (KeyCode::Char('z'), KeyModifiers::CONTROL) => {
583 if self.interface.undo() {
584 self.update_editor_from_interface();
585 if self.auto_preview {
586 self.update_preview();
587 }
588 }
589 Ok(PreviewAction::Continue)
590 },
591 (KeyCode::Char('y'), KeyModifiers::CONTROL) => {
592 if self.interface.redo() {
593 self.update_editor_from_interface();
594 if self.auto_preview {
595 self.update_preview();
596 }
597 }
598 Ok(PreviewAction::Continue)
599 },
600
601 _ if self.active_pane == ActivePane::Editor => {
603 self.handle_editor_key(key)
604 },
605
606 _ if self.active_pane == ActivePane::Preview => {
608 self.handle_preview_key(key)
609 },
610
611 _ => Ok(PreviewAction::Continue),
612 }
613 }
614
615 fn handle_editor_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
616 match key.code {
617 KeyCode::Up => {
618 if self.editor_pane.cursor_row > 0 {
619 self.editor_pane.cursor_row -= 1;
620 self.adjust_editor_scroll();
621 self.adjust_cursor_bounds();
622 }
623 }
624 KeyCode::Down => {
625 if self.editor_pane.cursor_row < self.editor_pane.content.len().saturating_sub(1) {
626 self.editor_pane.cursor_row += 1;
627 self.adjust_editor_scroll();
628 self.adjust_cursor_bounds();
629 }
630 }
631 KeyCode::Left => {
632 if self.editor_pane.cursor_col > 0 {
633 self.editor_pane.cursor_col -= 1;
634 } else if self.editor_pane.cursor_row > 0 {
635 self.editor_pane.cursor_row -= 1;
636 self.editor_pane.cursor_col = self.editor_pane.content
637 .get(self.editor_pane.cursor_row)
638 .map(|s| s.len())
639 .unwrap_or(0);
640 self.adjust_editor_scroll();
641 }
642 }
643 KeyCode::Right => {
644 let line_len = self.editor_pane.content
645 .get(self.editor_pane.cursor_row)
646 .map(|s| s.len())
647 .unwrap_or(0);
648
649 if self.editor_pane.cursor_col < line_len {
650 self.editor_pane.cursor_col += 1;
651 } else if self.editor_pane.cursor_row < self.editor_pane.content.len().saturating_sub(1) {
652 self.editor_pane.cursor_row += 1;
653 self.editor_pane.cursor_col = 0;
654 self.adjust_editor_scroll();
655 }
656 }
657 KeyCode::Home => {
658 self.editor_pane.cursor_col = 0;
659 }
660 KeyCode::End => {
661 self.editor_pane.cursor_col = self.editor_pane.content
662 .get(self.editor_pane.cursor_row)
663 .map(|s| s.len())
664 .unwrap_or(0);
665 }
666 KeyCode::Enter => {
667 self.insert_newline();
668 self.mark_editor_changed();
669 }
670 KeyCode::Char(c) => {
671 self.insert_char(c);
672 self.mark_editor_changed();
673 }
674 KeyCode::Backspace => {
675 self.delete_char();
676 self.mark_editor_changed();
677 }
678 KeyCode::Delete => {
679 self.delete_char_forward();
680 self.mark_editor_changed();
681 }
682 _ => {}
683 }
684
685 Ok(PreviewAction::Continue)
686 }
687
688 fn handle_preview_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
689 match key.code {
690 KeyCode::Up => {
691 if self.preview_pane.scroll_offset > 0 {
692 self.preview_pane.scroll_offset -= 1;
693 }
694 }
695 KeyCode::Down => {
696 self.preview_pane.scroll_offset += 1;
697 }
698 KeyCode::PageUp => {
699 self.preview_pane.scroll_offset = self.preview_pane.scroll_offset.saturating_sub(10);
700 }
701 KeyCode::PageDown => {
702 self.preview_pane.scroll_offset += 10;
703 }
704 KeyCode::Home => {
705 self.preview_pane.scroll_offset = 0;
706 }
707 _ => {}
708 }
709
710 Ok(PreviewAction::Continue)
711 }
712
713 fn cycle_layout(&mut self) {
714 self.layout = match self.layout {
715 SplitLayout::Horizontal => SplitLayout::Vertical,
716 SplitLayout::Vertical => SplitLayout::EditorOnly,
717 SplitLayout::EditorOnly => SplitLayout::PreviewOnly,
718 SplitLayout::PreviewOnly => SplitLayout::Horizontal,
719 };
720 self.update_pane_dimensions();
721 }
722
723 fn switch_active_pane(&mut self) {
724 self.active_pane = match self.active_pane {
725 ActivePane::Editor => ActivePane::Preview,
726 ActivePane::Preview => ActivePane::Editor,
727 };
728 }
729
730 fn update_pane_dimensions(&mut self) {
731 let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
732 let content_height = terminal_height.saturating_sub(6);
733
734 match self.layout {
735 SplitLayout::Horizontal => {
736 self.editor_pane.width = terminal_width / 2;
737 self.editor_pane.height = content_height;
738 self.preview_pane.width = terminal_width / 2;
739 self.preview_pane.height = content_height;
740 }
741 SplitLayout::Vertical => {
742 self.editor_pane.width = terminal_width;
743 self.editor_pane.height = content_height / 2;
744 self.preview_pane.width = terminal_width;
745 self.preview_pane.height = content_height / 2;
746 }
747 SplitLayout::EditorOnly => {
748 self.editor_pane.width = terminal_width;
749 self.editor_pane.height = content_height;
750 }
751 SplitLayout::PreviewOnly => {
752 self.preview_pane.width = terminal_width;
753 self.preview_pane.height = content_height;
754 }
755 }
756 }
757
758 fn should_refresh_preview(&self) -> bool {
759 self.last_edit.elapsed() >= self.preview_delay
760 }
761
762 pub fn update_preview(&mut self) {
763 let preview_builder = EditPreviewBuilder::new()
764 .with_diff(true)
765 .with_risk_assessment(true)
766 .with_syntax_highlighting(true);
767
768 let preview = preview_builder.build();
769 self.preview_pane.preview_result = Some(preview.generate_preview(&self.interface.modifiable_edit));
770 self.preview_pane.last_update = Instant::now();
771 }
772
773 fn mark_editor_changed(&mut self) {
774 self.last_edit = Instant::now();
775 }
776
777 fn save_editor_changes(&mut self) {
778 let new_content = self.editor_pane.content.join("\n");
779 let old_content = self.interface.modifiable_edit.compute_final_code();
780
781 if new_content != old_content {
782 self.interface.add_modification(
783 EditModification::CodeChange {
784 line: 0,
785 old: old_content,
786 new: new_content,
787 },
788 "Live editor changes".to_string(),
789 );
790 self.update_preview();
791 }
792 }
793
794 fn update_editor_from_interface(&mut self) {
795 let new_content = self.interface.modifiable_edit.compute_final_code();
796 self.editor_pane.content = new_content.lines().map(|s| s.to_string()).collect();
797 }
798
799 fn adjust_cursor_bounds(&mut self) {
800 if let Some(line) = self.editor_pane.content.get(self.editor_pane.cursor_row) {
801 if self.editor_pane.cursor_col > line.len() {
802 self.editor_pane.cursor_col = line.len();
803 }
804 }
805 }
806
807 fn adjust_editor_scroll(&mut self) {
808 let visible_lines = self.editor_pane.height as usize;
809
810 if self.editor_pane.cursor_row < self.editor_pane.scroll_offset {
811 self.editor_pane.scroll_offset = self.editor_pane.cursor_row;
812 } else if self.editor_pane.cursor_row >= self.editor_pane.scroll_offset + visible_lines {
813 self.editor_pane.scroll_offset = self.editor_pane.cursor_row - visible_lines + 1;
814 }
815 }
816
817 fn insert_char(&mut self, c: char) {
818 if self.editor_pane.cursor_row < self.editor_pane.content.len() {
819 self.editor_pane.content[self.editor_pane.cursor_row].insert(self.editor_pane.cursor_col, c);
820 self.editor_pane.cursor_col += 1;
821 }
822 }
823
824 fn insert_newline(&mut self) {
825 if self.editor_pane.cursor_row < self.editor_pane.content.len() {
826 let current_line = self.editor_pane.content[self.editor_pane.cursor_row].clone();
827 let (left, right) = current_line.split_at(self.editor_pane.cursor_col);
828
829 self.editor_pane.content[self.editor_pane.cursor_row] = left.to_string();
830 self.editor_pane.content.insert(self.editor_pane.cursor_row + 1, right.to_string());
831
832 self.editor_pane.cursor_row += 1;
833 self.editor_pane.cursor_col = 0;
834 self.adjust_editor_scroll();
835 }
836 }
837
838 fn delete_char(&mut self) {
839 if self.editor_pane.cursor_col > 0 && self.editor_pane.cursor_row < self.editor_pane.content.len() {
840 self.editor_pane.content[self.editor_pane.cursor_row].remove(self.editor_pane.cursor_col - 1);
841 self.editor_pane.cursor_col -= 1;
842 } else if self.editor_pane.cursor_row > 0 {
843 let current_line = self.editor_pane.content.remove(self.editor_pane.cursor_row);
845 self.editor_pane.cursor_row -= 1;
846 self.editor_pane.cursor_col = self.editor_pane.content[self.editor_pane.cursor_row].len();
847 self.editor_pane.content[self.editor_pane.cursor_row].push_str(¤t_line);
848 self.adjust_editor_scroll();
849 }
850 }
851
852 fn delete_char_forward(&mut self) {
853 if self.editor_pane.cursor_row < self.editor_pane.content.len() {
854 let line_len = self.editor_pane.content[self.editor_pane.cursor_row].len();
855 if self.editor_pane.cursor_col < line_len {
856 self.editor_pane.content[self.editor_pane.cursor_row].remove(self.editor_pane.cursor_col);
857 } else if self.editor_pane.cursor_row < self.editor_pane.content.len() - 1 {
858 let next_line = self.editor_pane.content.remove(self.editor_pane.cursor_row + 1);
860 self.editor_pane.content[self.editor_pane.cursor_row].push_str(&next_line);
861 }
862 }
863 }
864}
865
866#[derive(Debug)]
867enum PreviewAction {
868 Continue,
869 Save,
870 Exit,
871}
872
873#[cfg(test)]
874mod tests {
875 use super::*;
876 use crate::agents::gpt4_agent::ProposedEdit;
877 use crate::edit_control::ModifiableEdit;
878
879 fn create_test_interface() -> EditModificationInterface {
880 let proposed = ProposedEdit {
881 file: "test.rs".to_string(),
882 line_range: (10, 15),
883 new_code: "fn test() {\n println!(\"Hello, World!\");\n return 42;\n}".to_string(),
884 reason: "Test function for AdvancedPreviewSystem".to_string(),
885 confidence: 0.9,
886 };
887 let modifiable = ModifiableEdit::from_proposed_edit(proposed);
888 EditModificationInterface::new(modifiable)
889 }
890
891 #[test]
892 fn test_advanced_preview_system_creation() {
893 let interface = create_test_interface();
894 let preview_system = AdvancedPreviewSystem::new(interface);
895
896 assert_eq!(preview_system.layout, SplitLayout::Horizontal);
897 assert_eq!(preview_system.active_pane, ActivePane::Editor);
898 assert!(preview_system.auto_preview);
899 assert_eq!(preview_system.editor_pane.content.len(), 4); assert_eq!(preview_system.editor_pane.cursor_row, 0);
901 assert_eq!(preview_system.editor_pane.cursor_col, 0);
902 }
903
904 #[test]
905 fn test_layout_cycling() {
906 let interface = create_test_interface();
907 let mut preview_system = AdvancedPreviewSystem::new(interface);
908
909 assert_eq!(preview_system.layout, SplitLayout::Horizontal);
910
911 preview_system.cycle_layout();
912 assert_eq!(preview_system.layout, SplitLayout::Vertical);
913
914 preview_system.cycle_layout();
915 assert_eq!(preview_system.layout, SplitLayout::EditorOnly);
916
917 preview_system.cycle_layout();
918 assert_eq!(preview_system.layout, SplitLayout::PreviewOnly);
919
920 preview_system.cycle_layout();
921 assert_eq!(preview_system.layout, SplitLayout::Horizontal);
922 }
923
924 #[test]
925 fn test_pane_switching() {
926 let interface = create_test_interface();
927 let mut preview_system = AdvancedPreviewSystem::new(interface);
928
929 assert_eq!(preview_system.active_pane, ActivePane::Editor);
930
931 preview_system.switch_active_pane();
932 assert_eq!(preview_system.active_pane, ActivePane::Preview);
933
934 preview_system.switch_active_pane();
935 assert_eq!(preview_system.active_pane, ActivePane::Editor);
936 }
937
938 #[test]
939 fn test_editor_text_insertion() {
940 let interface = create_test_interface();
941 let mut preview_system = AdvancedPreviewSystem::new(interface);
942
943 let initial_line = preview_system.editor_pane.content[0].clone();
944
945 preview_system.insert_char('X');
946 assert_eq!(preview_system.editor_pane.cursor_col, 1);
947 assert!(preview_system.editor_pane.content[0].starts_with('X'));
948 assert_ne!(preview_system.editor_pane.content[0], initial_line);
949 }
950
951 #[test]
952 fn test_editor_newline_insertion() {
953 let interface = create_test_interface();
954 let mut preview_system = AdvancedPreviewSystem::new(interface);
955
956 let initial_lines = preview_system.editor_pane.content.len();
957
958 preview_system.insert_newline();
959 assert_eq!(preview_system.editor_pane.content.len(), initial_lines + 1);
960 assert_eq!(preview_system.editor_pane.cursor_row, 1);
961 assert_eq!(preview_system.editor_pane.cursor_col, 0);
962 }
963
964 #[test]
965 fn test_cursor_movement() {
966 let interface = create_test_interface();
967 let mut preview_system = AdvancedPreviewSystem::new(interface);
968
969 let initial_col = preview_system.editor_pane.cursor_col;
971 preview_system.editor_pane.cursor_col = 5;
972 preview_system.adjust_cursor_bounds();
973 assert!(preview_system.editor_pane.cursor_col >= initial_col);
974
975 preview_system.editor_pane.cursor_row = 1;
977 preview_system.adjust_cursor_bounds();
978 assert!(preview_system.editor_pane.cursor_row <= preview_system.editor_pane.content.len());
979 }
980
981 #[test]
982 fn test_scroll_adjustment() {
983 let interface = create_test_interface();
984 let mut preview_system = AdvancedPreviewSystem::new(interface);
985
986 preview_system.editor_pane.cursor_row = 50;
988 preview_system.editor_pane.height = 20; preview_system.adjust_editor_scroll();
990
991 assert!(preview_system.editor_pane.scroll_offset > 0);
993 assert!(preview_system.editor_pane.cursor_row >= preview_system.editor_pane.scroll_offset);
994 }
995
996 #[test]
997 fn test_auto_preview_timing() {
998 let interface = create_test_interface();
999 let mut preview_system = AdvancedPreviewSystem::new(interface);
1000
1001 assert!(!preview_system.should_refresh_preview());
1003
1004 preview_system.preview_delay = Duration::from_nanos(1);
1006 std::thread::sleep(Duration::from_millis(1));
1007
1008 assert!(preview_system.should_refresh_preview());
1010 }
1011
1012 #[test]
1013 fn test_preview_line_extraction() {
1014 let interface = create_test_interface();
1015 let mut preview_system = AdvancedPreviewSystem::new(interface);
1016
1017 preview_system.update_preview();
1018
1019 if let Some(ref preview) = preview_system.preview_pane.preview_result {
1020 let line = preview_system.get_preview_line(preview, 0);
1021 assert!(!line.is_empty());
1022 assert!(line.contains("╔═") || line.contains("║") || line.is_empty());
1024 }
1025 }
1026}