Skip to main content

split/
split.rs

1use crossterm::{
2    event::{
3        self, DisableMouseCapture, EnableMouseCapture, 
4        Event, KeyCode,
5    },
6    execute,
7    terminal::{
8        EnterAlternateScreen, LeaveAlternateScreen, 
9        disable_raw_mode, enable_raw_mode
10    },
11};
12use ratatui::{Terminal, backend::CrosstermBackend};
13use ratatui::layout::{Layout, Constraint, Direction, Rect, Position};
14use ratatui::widgets::{Block, Borders};
15use crossterm::event::MouseEvent;
16use std::io::stdout;
17use ratatui_code_editor::editor::Editor;
18use ratatui_code_editor::theme::vesper;
19
20fn main() -> anyhow::Result<()> {
21    let filename1 = "src/code.rs";
22    let filename2 = "src/editor.rs";
23    let language = "rust";
24    let content1 = std::fs::read_to_string(filename1).unwrap_or_default();
25    let content2 = std::fs::read_to_string(filename2).unwrap_or_default();
26
27    enable_raw_mode()?;
28    execute!(stdout(), EnterAlternateScreen)?;
29    execute!(stdout(), EnableMouseCapture)?;
30
31    let backend = CrosstermBackend::new(stdout());
32    let mut terminal = Terminal::new(backend)?;
33    
34    let theme = vesper();
35
36    let mut editor1 = Editor::new(&language, &content1, theme.clone())?;
37    let mut editor2 = Editor::new(&language, &content2, theme)?;
38
39    let mut editor1_area = ratatui::layout::Rect::default(); 
40    let mut editor2_area = ratatui::layout::Rect::default(); 
41
42    let mut active_editor = 0;
43
44    loop {
45        terminal.draw(|f| {
46            let chunks = Layout::default()
47                .direction(Direction::Horizontal)
48                .constraints([
49                    Constraint::Percentage(50),
50                    Constraint::Percentage(50)
51                ])
52                .split(f.area());
53
54            let block1 = Block::default()
55                .title(filename1)
56                .borders(Borders::ALL);
57            let block2 = Block::default()
58                .title(filename2)
59                .borders(Borders::ALL);
60
61            editor1_area = block1.inner(chunks[0]);
62            editor2_area = block2.inner(chunks[1]);
63
64            f.render_widget(block1, chunks[0]);
65            f.render_widget(block2, chunks[1]);
66            f.render_widget(&editor1, editor1_area);
67            f.render_widget(&editor2, editor2_area);
68            
69            let cursor = match active_editor {
70                0 => editor1.get_visible_cursor(&editor1_area),
71                _ => editor2.get_visible_cursor(&editor2_area),
72            };
73            
74            if let Some((x,y)) = cursor {
75                f.set_cursor_position(Position::new(x, y));
76            }
77        })?;
78
79        if event::poll(std::time::Duration::from_millis(100))? {
80            match event::read()? {
81                Event::Key(key) => {
82                    if key.code == KeyCode::Esc {
83                        break;
84                    } else if key.code == KeyCode::Tab {
85                        active_editor = (active_editor + 1) % 2;
86                    } else {
87                        match active_editor {
88                            0 => editor1.input(key, &editor1_area)?,
89                            1 => editor2.input(key, &editor2_area)?,
90                            _ => {}
91                        }
92                    }
93                }
94                Event::Mouse(mouse) => {
95                    if let Some(new_active) = detect_active_editor(&mouse, editor1_area, editor2_area) {
96                        active_editor = new_active;
97                    }
98
99                    match active_editor {
100                        0 => editor1.mouse(mouse, &editor1_area)?,
101                        1 => editor2.mouse(mouse, &editor2_area)?,
102                        _ => {}
103                    }
104                },
105
106                Event::Resize(_, _) => {}
107                _ => {}
108            }
109        }
110    }
111
112    disable_raw_mode()?;
113    execute!(
114        terminal.backend_mut(),
115        LeaveAlternateScreen,
116        DisableMouseCapture
117    )?;
118    Ok(())
119}
120
121fn detect_active_editor(
122    mouse: &MouseEvent, 
123    editor1_area: Rect, 
124    editor2_area: Rect
125) -> Option<usize> {
126    let x = mouse.column;
127    let y = mouse.row;
128
129    if rect_contains(editor1_area, x, y) {
130        Some(0)
131    } else if rect_contains(editor2_area, x, y) {
132        Some(1)
133    } else {
134        None
135    }
136}
137
138fn rect_contains(rect: Rect, x: u16, y: u16) -> bool {
139    x >= rect.x &&
140    x < rect.x + rect.width &&
141    y >= rect.y &&
142    y < rect.y + rect.height
143}