@@ -1,287 +1,69 @@
1−use anyhow::Error;
2−use termwiz::caps::Capabilities;
3−use termwiz::cell::AttributeChange;
4−use termwiz::color::{AnsiColor, ColorAttribute, RgbColor};
5−use termwiz::input::*;
6−use termwiz::surface::Change;
7−use termwiz::terminal::{buffered::BufferedTerminal, SystemTerminal, Terminal};
8−use termwiz::widgets::*;
1+use std::io::{self, stdout, Write};
92
10−use crate::Args;
3+use crossterm::{
4+ cursor,
5+ cursor::position,
6+ event::{self, read, Event, KeyCode, KeyEvent},
7+ execute, style,
8+ terminal::{self, disable_raw_mode, enable_raw_mode},
9+ Result,
10+};
1111
12−use std::{env, path::PathBuf};
12+const HELP: &str = r#"
13+ - Use q to quit
14+ - Move cursor with h, j, k, l
15+"#;
1316
14−use helix_core::Buffer;
17+pub struct Editor {}
1518
16−/// This is a widget for our application
17−pub struct MainScreen {}
18−
19−impl MainScreen {
20− pub fn new() -> Self {
21− Self {}
22− }
23−}
24−
25−impl Widget for MainScreen {
26− fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool {
27− true // handled it all
28− }
29−
30− /// Draw ourselves into the surface provided by RenderArgs
31− fn render(&mut self, args: &mut RenderArgs) {
32− // args.surface.add_change(Change::ClearScreen(
33− // ColorAttribute::TrueColorWithPaletteFallback(
34− // RgbColor::new(0x31, 0x1B, 0x92),
35− // AnsiColor::Black.into(),
36− // ),
37− // ));
38− // args.surface
39− // .add_change(Change::Attribute(AttributeChange::Foreground(
40− // ColorAttribute::TrueColorWithPaletteFallback(
41− // RgbColor::new(0xB3, 0x88, 0xFF),
42− // AnsiColor::Purple.into(),
43− // ),
44− // )));
45− }
46−
47− fn get_size_constraints(&self) -> layout::Constraints {
48− let mut constraints = layout::Constraints::default();
49− constraints.child_orientation = layout::ChildOrientation::Vertical;
50− constraints
51− }
52−}
53−
54−pub struct BufferComponent<'a> {
55− text: String,
56− buffer: &'a mut Buffer,
57−
58− first_line: usize,
59−}
60−
61−impl<'a> BufferComponent<'a> {
62− /// Initialize the widget with the input text
63− pub fn new(buffer: &'a mut Buffer) -> Self {
64− Self {
65− buffer,
66− text: String::new(),
67−
68− first_line: 0,
69− }
70− }
71−}
72−
73−impl<'a> Widget for BufferComponent<'a> {
74− fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool {
75− match event {
76− WidgetEvent::Input(InputEvent::Key(KeyEvent {
77− key: KeyCode::Char('k'),
78− ..
79− })) => {
80− self.first_line = self.first_line.saturating_sub(1);
81− }
82− WidgetEvent::Input(InputEvent::Key(KeyEvent {
83− key: KeyCode::Char('j'),
84− ..
85− })) => {
86− self.first_line = self.first_line.saturating_add(1);
87− }
88− WidgetEvent::Input(InputEvent::Key(KeyEvent {
89− key: KeyCode::Enter,
19+impl Editor {
20+ pub fn read_char() -> Result<char> {
21+ loop {
22+ if let Ok(Event::Key(KeyEvent {
23+ code: KeyCode::Char(c),
9024 ..
91− })) => {
92− self.text.push_str("\r\n");
25+ })) = event::read()
26+ {
27+ return Ok(c);
9328 }
94− WidgetEvent::Input(InputEvent::Paste(s)) => {
95− self.text.push_str(&s);
96− }
97− _ => {}
9829 }
99−
100− true // handled it all
10130 }
10231
103− /// Draw ourselves into the surface provided by RenderArgs
104− fn render(&mut self, args: &mut RenderArgs) {
105− args.surface
106− .add_change(Change::ClearScreen(ColorAttribute::Default));
107−
108− // args.surface
109− // .add_change(Change::Attribute(AttributeChange::Foreground(
110− // ColorAttribute::TrueColorWithPaletteFallback(
111− // RgbColor::new(0x11, 0x00, 0xFF),
112− // AnsiColor::Purple.into(),
113− // ),
114− // )));
115− let (_width, height) = args.surface.dimensions();
116−
117− for line in self.buffer.contents.lines_at(self.first_line).take(height) {
118− args.surface
119− .add_change(unsafe { String::from_utf8_unchecked(line.bytes().collect()) });
120− args.surface.add_change("\r");
121− }
122− // args.surface
123− // .add_change(format!("🤷 surface size is {:?}\r\n", dims));
124− // args.surface.add_change(self.text.clone());
125−
126− // Place the cursor at the end of the text.
127− // A more advanced text editing widget would manage the
128− // cursor position differently.
129− *args.cursor = CursorShapeAndPosition {
130− coords: args.surface.cursor_position().into(),
131− shape: termwiz::surface::CursorShape::SteadyBar,
132− ..Default::default()
133− };
134− }
135−
136− fn get_size_constraints(&self) -> layout::Constraints {
137− let mut c = layout::Constraints::default();
138− c.set_valign(layout::VerticalAlignment::Top);
139− c
140− }
141−}
142−
143−pub struct StatusLine {}
144−
145−impl StatusLine {
146− pub fn new() -> Self {
147− StatusLine {}
148− }
149−}
150−impl Widget for StatusLine {
151− fn process_event(&mut self, event: &WidgetEvent, _args: &mut UpdateArgs) -> bool {
152− true
153− }
154−
155− fn render(&mut self, args: &mut RenderArgs) {
156− args.surface.add_change(Change::ClearScreen(
157− ColorAttribute::TrueColorWithPaletteFallback(
158− RgbColor::new(0xFF, 0xFF, 0xFF),
159− AnsiColor::Black.into(),
160− ),
161− ));
162− args.surface
163− .add_change(Change::Attribute(AttributeChange::Foreground(
164− ColorAttribute::TrueColorWithPaletteFallback(
165− RgbColor::new(0x00, 0x00, 0x00),
166− AnsiColor::Black.into(),
167− ),
168− )));
169−
170− args.surface.add_change(" helix");
171− }
172−
173− fn get_size_constraints(&self) -> layout::Constraints {
174− *layout::Constraints::default()
175− .set_fixed_height(1)
176− .set_valign(layout::VerticalAlignment::Bottom)
177− }
178−}
179−
180−pub struct Editor {
181− terminal: BufferedTerminal<SystemTerminal>,
182−
183− buffer: Option<Buffer>,
184−}
185−
186−impl Editor {
187− pub fn new(mut args: Args) -> Result<Self, Error> {
188− // Create a terminal
189− let caps = Capabilities::new_from_env()?;
190− let mut terminal = BufferedTerminal::new(SystemTerminal::new(caps)?)?;
191−
192− let mut editor = Editor {
193− terminal,
194− buffer: None,
195− };
196−
197− if let Some(file) = args.files.pop() {
198− editor.open(file)?;
32+ pub fn print_events() -> Result<()> {
33+ loop {
34+ // Handle key events
35+ match Editor::read_char()? {
36+ 'h' => execute!(io::stdout(), cursor::MoveLeft(1))?,
37+ 'j' => execute!(io::stdout(), cursor::MoveDown(1))?,
38+ 'k' => execute!(io::stdout(), cursor::MoveUp(1))?,
39+ 'l' => execute!(io::stdout(), cursor::MoveRight(1))?,
40+ 'q' => {
41+ execute!(
42+ io::stdout(),
43+ style::ResetColor,
44+ cursor::Show,
45+ terminal::LeaveAlternateScreen
46+ )?;
47+ break;
48+ }
49+ _ => println!("use 'q' to quit."),
50+ }
19951 }
20052
201− Ok(editor)
202− }
203−
204− pub fn open(&mut self, path: PathBuf) -> Result<(), Error> {
205− let buffer = Buffer::load(path)?;
206− self.buffer = Some(buffer);
20753 Ok(())
20854 }
20955
210− pub fn run(&mut self) -> Result<(), Error> {
211− // Start with an empty string; typing into the app will
212− // update this string.
213− let mut typed_text = String::new();
214−
215− {
216− let buf = &mut self.terminal;
217− // Put the terminal in raw mode + alternate screen
218− buf.terminal().enter_alternate_screen()?;
219− buf.terminal().set_raw_mode()?;
220−
221− // Set up the UI
222− let mut ui = Ui::new();
223−
224− let root_id = ui.set_root(MainScreen::new());
225− let buffer_id =
226− ui.add_child(root_id, BufferComponent::new(self.buffer.as_mut().unwrap()));
227− // let root_id = ui.set_root(Buffer::new(&mut typed_text));
228− ui.add_child(root_id, StatusLine::new());
229− ui.set_focus(buffer_id);
56+ pub fn run() -> Result<()> {
57+ enable_raw_mode()?;
23058
231− loop {
232− ui.process_event_queue()?;
233−
234− // After updating and processing all of the widgets, compose them
235− // and render them to the screen.
236− if ui.render_to_screen(buf)? {
237− // We have more events to process immediately; don't block waiting
238− // for input below, but jump to the top of the loop to re-run the
239− // updates.
240− continue;
241− }
242− // Compute an optimized delta to apply to the terminal and display it
243− buf.flush()?;
244−
245− // Wait for user input
246− match buf.terminal().poll_input(None) {
247− Ok(Some(InputEvent::Resized { rows, cols })) => {
248− // FIXME: this is working around a bug where we don't realize
249− // that we should redraw everything on resize in BufferedTerminal.
250− buf.add_change(Change::ClearScreen(Default::default()));
251− buf.resize(cols, rows);
252− }
253− Ok(Some(input)) => match input {
254− InputEvent::Key(KeyEvent {
255− key: KeyCode::Escape,
256− ..
257− }) => {
258− // Quit the app when escape is pressed
259− break;
260− }
261− input @ _ => {
262− // Feed input into the Ui
263− ui.queue_event(WidgetEvent::Input(input));
264− }
265− },
266− Ok(None) => {}
267− Err(e) => {
268− print!("{:?}\r\n", e);
269− break;
270− }
271− }
272− }
59+ // used for clearing the screen
60+ execute!(io::stdout(), terminal::EnterAlternateScreen)?;
61+ println!("{}", HELP);
62+ let mut stdout = stdout();
63+ if let Err(e) = Editor::print_events() {
64+ println!("Error: {:?}\r", e);
27365 }
27466
275− // After we've stopped the full screen raw terminal,
276− // print out the final edited value of the input text.
277− println!("The text you entered: {}", typed_text);
278− Ok(())
279− }
280−
281− pub fn render(&self) {
282− // create a new window sized surface
283− // paint all components
284− // diff vs last frame, swap
285− // paint diff
67+ disable_raw_mode()
28668 }
28769}