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

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::{
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();
}

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::{
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<Editor>,
selected: Option<usize>,
clipboard: Clipboard,
_term: RawTerminal<Stdout>,
}
impl Navigator {
fn new() -> Self {
let term = stdout().into_raw_mode().unwrap();
let clipboard = Clipboard::new();
let mut editors: Vec<Editor> = 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);
}
}

View file

@ -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<String> {
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())