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}