diff --git a/Cargo.lock b/Cargo.lock index e53dcd8..efe917a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,49 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossterm" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + [[package]] name = "libc" version = "0.2.139" @@ -18,14 +55,62 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" name = "lili" version = "0.1.0" dependencies = [ - "termion", + "crossterm", ] [[package]] -name = "numtoa" -version = "0.1.0" +name = "lock_api" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] [[package]] name = "redox_syscall" @@ -37,22 +122,137 @@ dependencies = [ ] [[package]] -name = "redox_termios" -version = "0.1.2" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "signal-hook" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ - "redox_syscall", + "libc", + "signal-hook-registry", ] [[package]] -name = "termion" -version = "2.0.1" +name = "signal-hook-mio" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", - "numtoa", - "redox_syscall", - "redox_termios", + "mio", + "signal-hook", ] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/Cargo.toml b/Cargo.toml index b76decc..b73d82f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -termion = "2.0.1" +crossterm = "0.26.1" diff --git a/src/editor.rs b/src/editor.rs index 6549e6f..23ce325 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,15 +1,16 @@ +use crossterm::{ + cursor::{self, MoveTo}, + event::{self, Event, KeyCode, KeyModifiers}, + queue, + style::{Color, Colors, ResetColor, SetColors}, + terminal::{self, Clear, ClearType}, +}; use std::{ fs::{self, File}, - io::{stdin, stdout, Write}, + io::{stdout, Write}, ops::Range, vec, }; -use termion::{ - clear, color, cursor, - event::{Event, Key}, - input::TermRead, - terminal_size, -}; use crate::util::read_line; use crate::{clipboard::Clipboard, util::RangeConverter}; @@ -87,34 +88,39 @@ impl Editor { } fn input(&mut self) { - for event in stdin().events().take(1).flatten() { - if let Event::Key(key) = event { - match key { - Key::Esc => self.active = false, - Key::Char(char) => self.insert_char(char), - Key::Backspace => self.backspace(), - Key::Delete => self.delete(), - Key::Left => self.move_left(), - Key::Right => self.move_right(), - Key::Up => self.move_up(), - Key::Down => self.move_down(), - Key::Home => self.move_home(), - Key::End => self.move_end(), - Key::Ctrl('s') => self.save(), - Key::Ctrl('p') => self.toggle_marker(), - Key::Ctrl('c') => self.copy(), - Key::Ctrl('x') => self.cut(), - Key::Ctrl('v') => self.paste(), + match event::read() { + Ok(Event::Key(event)) => match event.modifiers { + KeyModifiers::NONE => match event.code { + KeyCode::Esc => self.active = false, + KeyCode::Char(ch) => self.insert_char(ch), + KeyCode::Backspace => self.backspace(), + KeyCode::Delete => self.delete(), + KeyCode::Left => self.move_left(), + KeyCode::Right => self.move_right(), + KeyCode::Up => self.move_up(), + KeyCode::Down => self.move_down(), + KeyCode::Home => self.move_home(), + KeyCode::End => self.move_end(), _ => (), - } - } + }, + KeyModifiers::CONTROL => match event.code { + KeyCode::Char('s') => self.save(), + KeyCode::Char('p') => self.toggle_marker(), + KeyCode::Char('c') => self.copy(), + KeyCode::Char('x') => self.cut(), + KeyCode::Char('v') => self.paste(), + _ => (), + }, + _ => (), + }, + _ => (), } } fn draw(&self) { - print!("{}", clear::All); + queue!(stdout(), Clear(ClearType::All)).unwrap(); - let max_rows = terminal_size().unwrap().1 as usize - 1; + let max_rows = terminal::size().unwrap().1 as usize - 1; let end = (self.scroll + max_rows).min(self.lines.len()); let visible_rows = self.scroll..end; @@ -125,7 +131,7 @@ impl Editor { for (line_index, line) in self.lines[visible_rows].iter().enumerate() { let text = &self.text[line.clone()]; - print!("{}", cursor::Goto(1, line_index as u16 + 1)); + queue!(stdout(), MoveTo(0, line_index as u16)).unwrap(); if self.marker.is_none() { print!("{}", text.replace('\t', &" ".repeat(TAB_SIZE))); @@ -150,21 +156,22 @@ impl Editor { } } self.status_line(); - print!( - "{}{}", - cursor::Goto( - self.physical_column() as u16 + 1, - (self.cursor.line - self.scroll) as u16 + 1 + queue!( + stdout(), + MoveTo( + self.physical_column() as u16, + (self.cursor.line - self.scroll) as u16 ), cursor::Show - ); + ) + .unwrap(); stdout().flush().unwrap(); } fn status_line(&self) { + queue!(stdout(), MoveTo(0, terminal::size().unwrap().1)).unwrap(); print!( - "{}({}, {}) {}", - cursor::Goto(1, terminal_size().unwrap().1), + "({},{}) {}", self.cursor.line, self.physical_column(), self.name(), @@ -213,7 +220,7 @@ impl Editor { self.cursor.line += 1; self.cursor.column = physical_column.min(self.current_line().len()); self.ensure_char_boundary(); - if self.cursor.line > (self.scroll + terminal_size().unwrap().1 as usize - 2) { + if self.cursor.line > (self.scroll + terminal::size().unwrap().1 as usize - 2) { self.scroll += 1; } } @@ -379,13 +386,9 @@ impl Editor { } fn color_selection() { - print!( - "{}{}", - color::Fg(color::Black), - color::Bg(color::LightBlack) - ); + queue!(stdout(), SetColors(Colors::new(Color::Black, Color::White))).unwrap(); } fn color_reset() { - print!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset)); + queue!(stdout(), ResetColor).unwrap(); } diff --git a/src/main.rs b/src/main.rs index ddfac64..92c030e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,18 @@ +use crossterm::{ + cursor::{self, MoveTo}, + event::{self, Event, KeyCode, KeyModifiers}, + execute, queue, + style::{Color, Colors, ResetColor, SetColors}, + terminal::{ + disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, + LeaveAlternateScreen, + }, +}; use std::{ env, - io::{stdin, stdout, Stdout, Write}, + io::{stdout, Write}, process::exit, }; -use termion::{ - clear, color, - cursor::{self, Goto}, - event::{Event, Key}, - input::TermRead, - raw::{IntoRawMode, RawTerminal}, -}; mod clipboard; mod editor; @@ -25,12 +28,10 @@ struct Navigator { editors: Vec, selected: Option, clipboard: Clipboard, - _term: RawTerminal, } impl Navigator { fn new() -> Self { - let term = stdout().into_raw_mode().unwrap(); let clipboard = Clipboard::new(); let mut editors: Vec = env::args() .skip(1) @@ -43,13 +44,12 @@ impl Navigator { editors, selected: Some(0), clipboard, - _term: term, } } fn run(mut self) { - print!("{}", clear::All); - stdout().flush().unwrap(); + execute!(stdout(), EnterAlternateScreen, Clear(ClearType::All)).unwrap(); + enable_raw_mode().unwrap(); loop { self.draw(); @@ -58,41 +58,38 @@ impl Navigator { } fn draw(&self) { - print!( - "{}{}{}Open editors: {}", - clear::All, - cursor::Hide, - Goto(1, 1), - self.editors.len() - ); + queue!(stdout(), Clear(ClearType::All), cursor::Hide, MoveTo(0, 0)).unwrap(); + print!("Open editors: {}", self.editors.len()); for (index, editor) in self.editors.iter().enumerate() { if Some(index) == self.selected { - print!("{}{}", color::Fg(color::Black), color::Bg(color::White)); + queue!(stdout(), SetColors(Colors::new(Color::Black, Color::White))).unwrap(); } + queue!(stdout(), MoveTo(1, index as u16 + 1)).unwrap(); print!( - "{}{}{}", - Goto(2, index as u16 + 2), + "{}{}", editor.has_unsaved_changes().then_some("*").unwrap_or(" "), editor.name() ); - print!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset)); + queue!(stdout(), ResetColor).unwrap(); } stdout().flush().unwrap(); } fn input(&mut self) { - for event in stdin().events().take(1).flatten() { - if let Event::Key(key) = event { - match key { - Key::Char('q') => self.quit(), - Key::Char('\n') => self.open_selected(), - Key::Ctrl('n') => self.new_editor(), - Key::Up => self.nav_up(), - Key::Down => self.nav_down(), - _ => (), + if let Ok(Event::Key(event)) = event::read() { + match event.code { + KeyCode::Char('q') => self.quit(), + KeyCode::Up => self.nav_up(), + KeyCode::Down => self.nav_down(), + KeyCode::Enter => self.open_selected(), + KeyCode::Char('n') => { + if event.modifiers == KeyModifiers::CONTROL { + self.new_editor(); + } } + _ => (), } } } @@ -124,7 +121,8 @@ impl Navigator { } fn quit(&self) { - print!("{}{}", clear::All, cursor::Show); + disable_raw_mode().unwrap(); + execute!(stdout(), LeaveAlternateScreen, cursor::Show).unwrap(); exit(0); } } diff --git a/src/util.rs b/src/util.rs index e2bb524..a3fb70a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,37 +1,36 @@ -use std::{ - io::{stdin, stdout, Write}, - ops::Range, -}; -use termion::{ +use crossterm::{ cursor, - event::{Event, Key}, - input::TermRead, - terminal_size, + event::{self, Event, KeyCode}, + queue, terminal, +}; +use std::{ + io::{stdout, Write}, + ops::Range, }; pub fn read_line(prompt: &str) -> Option { let mut response = String::new(); - let size = terminal_size().unwrap(); - let start_pos = cursor::Goto(1, size.1); - let width = size.0 as usize; + let size = terminal::size().unwrap(); + let start_pos = cursor::MoveTo(0, size.1); - print!("{start_pos}{prompt}{response}",); + queue!(stdout(), start_pos).unwrap(); + print!("{prompt}"); stdout().flush().unwrap(); - for event in stdin().events() { - if let Ok(Event::Key(key)) = event { - match key { - Key::Char('\n') => break, - Key::Char(ch) => response.push(ch), - Key::Backspace => { + loop { + if let Ok(Event::Key(event)) = event::read() { + match event.code { + KeyCode::Enter => break, + KeyCode::Char(ch) => response.push(ch), + KeyCode::Backspace => { response.pop(); - print!("{start_pos}{:width$}", " "); } - Key::Esc => return None, + KeyCode::Esc => return None, _ => (), } } - print!("{start_pos}{prompt}{response}",); + queue!(stdout(), start_pos).unwrap(); + print!("{prompt}{response} "); stdout().flush().unwrap(); } Some(response.trim().into())