port to crossterm

This commit is contained in:
Crispy 2023-03-04 11:26:57 +01:00
parent f3f1c4de94
commit f5da786053
5 changed files with 315 additions and 115 deletions

228
Cargo.lock generated
View file

@ -2,12 +2,49 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 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]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.139"
@ -18,14 +55,62 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
name = "lili" name = "lili"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"termion", "crossterm",
] ]
[[package]] [[package]]
name = "numtoa" name = "lock_api"
version = "0.1.0" version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "redox_syscall" name = "redox_syscall"
@ -37,22 +122,137 @@ dependencies = [
] ]
[[package]] [[package]]
name = "redox_termios" name = "scopeguard"
version = "0.1.2" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"redox_syscall", "libc",
"signal-hook-registry",
] ]
[[package]] [[package]]
name = "termion" name = "signal-hook-mio"
version = "2.0.1" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [ dependencies = [
"libc", "libc",
"numtoa", "mio",
"redox_syscall", "signal-hook",
"redox_termios",
] ]
[[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"

View file

@ -6,4 +6,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
termion = "2.0.1" crossterm = "0.26.1"

View file

@ -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::{ use std::{
fs::{self, File}, fs::{self, File},
io::{stdin, stdout, Write}, io::{stdout, Write},
ops::Range, ops::Range,
vec, vec,
}; };
use termion::{
clear, color, cursor,
event::{Event, Key},
input::TermRead,
terminal_size,
};
use crate::util::read_line; use crate::util::read_line;
use crate::{clipboard::Clipboard, util::RangeConverter}; use crate::{clipboard::Clipboard, util::RangeConverter};
@ -87,34 +88,39 @@ impl Editor {
} }
fn input(&mut self) { fn input(&mut self) {
for event in stdin().events().take(1).flatten() { match event::read() {
if let Event::Key(key) = event { Ok(Event::Key(event)) => match event.modifiers {
match key { KeyModifiers::NONE => match event.code {
Key::Esc => self.active = false, KeyCode::Esc => self.active = false,
Key::Char(char) => self.insert_char(char), KeyCode::Char(ch) => self.insert_char(ch),
Key::Backspace => self.backspace(), KeyCode::Backspace => self.backspace(),
Key::Delete => self.delete(), KeyCode::Delete => self.delete(),
Key::Left => self.move_left(), KeyCode::Left => self.move_left(),
Key::Right => self.move_right(), KeyCode::Right => self.move_right(),
Key::Up => self.move_up(), KeyCode::Up => self.move_up(),
Key::Down => self.move_down(), KeyCode::Down => self.move_down(),
Key::Home => self.move_home(), KeyCode::Home => self.move_home(),
Key::End => self.move_end(), KeyCode::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(),
_ => (), _ => (),
} },
} 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) { 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 end = (self.scroll + max_rows).min(self.lines.len());
let visible_rows = self.scroll..end; let visible_rows = self.scroll..end;
@ -125,7 +131,7 @@ impl Editor {
for (line_index, line) in self.lines[visible_rows].iter().enumerate() { for (line_index, line) in self.lines[visible_rows].iter().enumerate() {
let text = &self.text[line.clone()]; 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() { if self.marker.is_none() {
print!("{}", text.replace('\t', &" ".repeat(TAB_SIZE))); print!("{}", text.replace('\t', &" ".repeat(TAB_SIZE)));
@ -150,21 +156,22 @@ impl Editor {
} }
} }
self.status_line(); self.status_line();
print!( queue!(
"{}{}", stdout(),
cursor::Goto( MoveTo(
self.physical_column() as u16 + 1, self.physical_column() as u16,
(self.cursor.line - self.scroll) as u16 + 1 (self.cursor.line - self.scroll) as u16
), ),
cursor::Show cursor::Show
); )
.unwrap();
stdout().flush().unwrap(); stdout().flush().unwrap();
} }
fn status_line(&self) { fn status_line(&self) {
queue!(stdout(), MoveTo(0, terminal::size().unwrap().1)).unwrap();
print!( print!(
"{}({}, {}) {}", "({},{}) {}",
cursor::Goto(1, terminal_size().unwrap().1),
self.cursor.line, self.cursor.line,
self.physical_column(), self.physical_column(),
self.name(), self.name(),
@ -213,7 +220,7 @@ impl Editor {
self.cursor.line += 1; self.cursor.line += 1;
self.cursor.column = physical_column.min(self.current_line().len()); self.cursor.column = physical_column.min(self.current_line().len());
self.ensure_char_boundary(); 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; self.scroll += 1;
} }
} }
@ -379,13 +386,9 @@ impl Editor {
} }
fn color_selection() { fn color_selection() {
print!( queue!(stdout(), SetColors(Colors::new(Color::Black, Color::White))).unwrap();
"{}{}",
color::Fg(color::Black),
color::Bg(color::LightBlack)
);
} }
fn color_reset() { fn color_reset() {
print!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset)); queue!(stdout(), ResetColor).unwrap();
} }

View file

@ -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::{ use std::{
env, env,
io::{stdin, stdout, Stdout, Write}, io::{stdout, Write},
process::exit, process::exit,
}; };
use termion::{
clear, color,
cursor::{self, Goto},
event::{Event, Key},
input::TermRead,
raw::{IntoRawMode, RawTerminal},
};
mod clipboard; mod clipboard;
mod editor; mod editor;
@ -25,12 +28,10 @@ struct Navigator {
editors: Vec<Editor>, editors: Vec<Editor>,
selected: Option<usize>, selected: Option<usize>,
clipboard: Clipboard, clipboard: Clipboard,
_term: RawTerminal<Stdout>,
} }
impl Navigator { impl Navigator {
fn new() -> Self { fn new() -> Self {
let term = stdout().into_raw_mode().unwrap();
let clipboard = Clipboard::new(); let clipboard = Clipboard::new();
let mut editors: Vec<Editor> = env::args() let mut editors: Vec<Editor> = env::args()
.skip(1) .skip(1)
@ -43,13 +44,12 @@ impl Navigator {
editors, editors,
selected: Some(0), selected: Some(0),
clipboard, clipboard,
_term: term,
} }
} }
fn run(mut self) { fn run(mut self) {
print!("{}", clear::All); execute!(stdout(), EnterAlternateScreen, Clear(ClearType::All)).unwrap();
stdout().flush().unwrap(); enable_raw_mode().unwrap();
loop { loop {
self.draw(); self.draw();
@ -58,41 +58,38 @@ impl Navigator {
} }
fn draw(&self) { fn draw(&self) {
print!( queue!(stdout(), Clear(ClearType::All), cursor::Hide, MoveTo(0, 0)).unwrap();
"{}{}{}Open editors: {}", print!("Open editors: {}", self.editors.len());
clear::All,
cursor::Hide,
Goto(1, 1),
self.editors.len()
);
for (index, editor) in self.editors.iter().enumerate() { for (index, editor) in self.editors.iter().enumerate() {
if Some(index) == self.selected { 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!( print!(
"{}{}{}", "{}{}",
Goto(2, index as u16 + 2),
editor.has_unsaved_changes().then_some("*").unwrap_or(" "), editor.has_unsaved_changes().then_some("*").unwrap_or(" "),
editor.name() editor.name()
); );
print!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset)); queue!(stdout(), ResetColor).unwrap();
} }
stdout().flush().unwrap(); stdout().flush().unwrap();
} }
fn input(&mut self) { fn input(&mut self) {
for event in stdin().events().take(1).flatten() { if let Ok(Event::Key(event)) = event::read() {
if let Event::Key(key) = event { match event.code {
match key { KeyCode::Char('q') => self.quit(),
Key::Char('q') => self.quit(), KeyCode::Up => self.nav_up(),
Key::Char('\n') => self.open_selected(), KeyCode::Down => self.nav_down(),
Key::Ctrl('n') => self.new_editor(), KeyCode::Enter => self.open_selected(),
Key::Up => self.nav_up(), KeyCode::Char('n') => {
Key::Down => self.nav_down(), if event.modifiers == KeyModifiers::CONTROL {
_ => (), self.new_editor();
}
} }
_ => (),
} }
} }
} }
@ -124,7 +121,8 @@ impl Navigator {
} }
fn quit(&self) { fn quit(&self) {
print!("{}{}", clear::All, cursor::Show); disable_raw_mode().unwrap();
execute!(stdout(), LeaveAlternateScreen, cursor::Show).unwrap();
exit(0); exit(0);
} }
} }

View file

@ -1,37 +1,36 @@
use std::{ use crossterm::{
io::{stdin, stdout, Write},
ops::Range,
};
use termion::{
cursor, cursor,
event::{Event, Key}, event::{self, Event, KeyCode},
input::TermRead, queue, terminal,
terminal_size, };
use std::{
io::{stdout, Write},
ops::Range,
}; };
pub fn read_line(prompt: &str) -> Option<String> { pub fn read_line(prompt: &str) -> Option<String> {
let mut response = String::new(); let mut response = String::new();
let size = terminal_size().unwrap(); let size = terminal::size().unwrap();
let start_pos = cursor::Goto(1, size.1); let start_pos = cursor::MoveTo(0, size.1);
let width = size.0 as usize;
print!("{start_pos}{prompt}{response}",); queue!(stdout(), start_pos).unwrap();
print!("{prompt}");
stdout().flush().unwrap(); stdout().flush().unwrap();
for event in stdin().events() { loop {
if let Ok(Event::Key(key)) = event { if let Ok(Event::Key(event)) = event::read() {
match key { match event.code {
Key::Char('\n') => break, KeyCode::Enter => break,
Key::Char(ch) => response.push(ch), KeyCode::Char(ch) => response.push(ch),
Key::Backspace => { KeyCode::Backspace => {
response.pop(); 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(); stdout().flush().unwrap();
} }
Some(response.trim().into()) Some(response.trim().into())