aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-01-12 08:31:12 -0800
committerRose Hogenson <rosehogenson@posteo.net>2024-01-12 08:31:12 -0800
commitd333343b15aab5747ccf74c495cb085b4e32b029 (patch)
tree0c523f40929f44cd2f633d9bfe989f9305f29f41 /src/main.rs
parente23b1c7254aa21c0eb2fd18f720df262725c6d81 (diff)
downloadeditor-d333343b15aab5747ccf74c495cb085b4e32b029.tar.zst
Fix a bug where cursor_pos would return old data.
For now I'm just counting the failures, and aborting if cursor_pos immediately failed before. We have to skip the incoming status reports with read_key before cursor_pos will start working again. This should flush any old status reports, and prevent reading them incorrectly.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs118
1 files changed, 61 insertions, 57 deletions
diff --git a/src/main.rs b/src/main.rs
index 9c30520..b40d9b1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,9 +5,10 @@ use rope::Rope;
use std::error::Error;
use std::ffi::{OsStr, OsString};
use std::fs::File;
-use std::io::{stderr, stdout, BufReader, Stdin, Write};
+use std::io::{stderr, stdout, Write};
use std::path::{Path, PathBuf};
use term::Key;
+use term::Reader;
fn open(file: &Path) -> Result<Rope, std::io::Error> {
let mut f = File::open(file)?;
@@ -15,11 +16,7 @@ fn open(file: &Path) -> Result<Rope, std::io::Error> {
Ok(r)
}
-fn line_offset(
- stdin: &mut BufReader<Stdin>,
- line: &Rope,
- col: u16,
-) -> Result<usize, Box<dyn Error>> {
+fn line_offset(stdin: &mut Reader, line: &Rope, col: u16) -> Result<usize, Box<dyn Error>> {
let mut n = 0;
for i in 0.. {
if i == line.len() {
@@ -49,7 +46,7 @@ fn line_offset(
}
write!(stdout(), "\x1b[G")?;
line.slice(0, h).print(&mut stdout())?;
- let (_, c) = term::cursor_pos(stdin)?;
+ let (_, c) = stdin.cursor_pos()?;
if c <= col {
lo = h + 1;
if lo < line.len() {
@@ -64,14 +61,14 @@ fn line_offset(
}
if lo == 0 {
// You might think this case is impossible (I did), but terminals can do a lot of
- // weird things.
- return Ok(0);
+ // weird things and I'm trying to make this robust.
+ return Err(Box::from("incorrect cursor_pos"));
}
Ok(line.floor_char_boundary(lo - 1))
}
fn floor_grapheme_cluster(
- stdin: &mut BufReader<Stdin>,
+ stdin: &mut Reader,
line: &Rope,
offset: usize,
) -> Result<usize, Box<dyn Error>> {
@@ -82,11 +79,11 @@ fn floor_grapheme_cluster(
write!(stdout(), "\x1b[G")?;
line.slice(0, line.ceil_char_boundary(offset + 1))
.print(&mut stdout())?;
- let (_, target_col) = term::cursor_pos(stdin)?;
+ let (_, target_col) = stdin.cursor_pos()?;
while offset > 0 {
write!(stdout(), "\x1b[G")?;
line.slice(0, offset).print(&mut stdout())?;
- let (_, col) = term::cursor_pos(stdin)?;
+ let (_, col) = stdin.cursor_pos()?;
if col < target_col {
return Ok(offset);
}
@@ -96,7 +93,7 @@ fn floor_grapheme_cluster(
}
fn ceil_grapheme_cluster(
- stdin: &mut BufReader<Stdin>,
+ stdin: &mut Reader,
line: &Rope,
offset: usize,
) -> Result<usize, Box<dyn Error>> {
@@ -106,7 +103,7 @@ fn ceil_grapheme_cluster(
}
write!(stdout(), "\x1b[G")?;
line.slice(0, offset).print(&mut stdout())?;
- let (_, target_col) = term::cursor_pos(stdin)?;
+ let (_, target_col) = stdin.cursor_pos()?;
loop {
if offset >= line.len() {
return Ok(line.len());
@@ -114,7 +111,7 @@ fn ceil_grapheme_cluster(
let old_offset = offset;
offset = line.ceil_char_boundary(old_offset + 1);
line.slice(old_offset, offset).print(&mut stdout())?;
- let (_, col) = term::cursor_pos(stdin)?;
+ let (_, col) = stdin.cursor_pos()?;
if col > target_col {
return Ok(line.floor_char_boundary(offset - 1));
}
@@ -139,6 +136,7 @@ struct State {
cursor_row: u16,
cursor_col: u16,
+ stdin: Reader,
path: PathBuf,
inserting: bool,
need_repaint_screen: bool,
@@ -150,12 +148,12 @@ impl State {
self.buf.line(self.row_start + usize::from(self.cursor_row))
}
- fn update_cursor_col(&mut self, stdin: &mut BufReader<Stdin>) -> Result<(), Box<dyn Error>> {
+ fn update_cursor_col(&mut self) -> Result<(), Box<dyn Error>> {
let size = term::size()?;
let line = self.line();
write!(stdout(), "\x1b[{}H", self.cursor_row + 1)?;
line.slice(0, self.line_offset).print(&mut stdout())?;
- let (_, col) = term::cursor_pos(stdin)?;
+ let (_, col) = self.stdin.cursor_pos()?;
if col == size.ws_col - 1 {
self.cursor_col = col - 1;
write!(
@@ -237,7 +235,7 @@ impl State {
self.history.push(self.snapshot());
}
- fn keypress(&mut self, stdin: &mut BufReader<Stdin>, key: Key) -> Result<bool, Box<dyn Error>> {
+ fn keypress(&mut self, key: Key) -> Result<bool, Box<dyn Error>> {
const CTRL_Q: u8 = b'Q' - b'@';
const CTRL_S: u8 = b'S' - b'@';
const CTRL_Y: u8 = b'Y' - b'@';
@@ -260,7 +258,7 @@ impl State {
self.repaint_screen()?;
self.need_repaint_screen = false;
}
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
self.need_repaint_line = false;
stdout().flush()?;
}
@@ -272,17 +270,17 @@ impl State {
.buf
.line(self.row_start + usize::from(self.cursor_row) - 1);
if self.cursor_row == 0 {
- self.line_offset = line_offset(stdin, &prev_line, self.cursor_col)?;
+ self.line_offset = line_offset(&mut self.stdin, &prev_line, self.cursor_col)?;
self.row_start -= 1;
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
write!(stdout(), "\x1b[{}H", self.cursor_row)?;
- self.line_offset = line_offset(stdin, &prev_line, self.cursor_col)?;
+ self.line_offset = line_offset(&mut self.stdin, &prev_line, self.cursor_col)?;
self.cursor_row -= 1;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::Down => {
@@ -295,23 +293,24 @@ impl State {
.line(self.row_start + usize::from(self.cursor_row) + 1);
if self.cursor_row < size.ws_row - 1 {
write!(stdout(), "\x1b[{}H", self.cursor_row + 2)?;
- self.line_offset = line_offset(stdin, &next_line, self.cursor_col)?;
+ self.line_offset = line_offset(&mut self.stdin, &next_line, self.cursor_col)?;
self.cursor_row += 1;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
- self.line_offset = line_offset(stdin, &next_line, self.cursor_col)?;
+ self.line_offset = line_offset(&mut self.stdin, &next_line, self.cursor_col)?;
self.row_start += 1;
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::Left => {
if self.line_offset > 0 {
+ let line = self.line();
self.line_offset =
- floor_grapheme_cluster(stdin, &self.line(), self.line_offset - 1)?;
- self.update_cursor_col(stdin)?;
+ floor_grapheme_cluster(&mut self.stdin, &line, self.line_offset - 1)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
@@ -319,14 +318,14 @@ impl State {
let size = term::size()?;
write!(stdout(), "\x1b[{}H", self.cursor_row)?;
self.line_offset = line_offset(
- stdin,
+ &mut self.stdin,
&self
.buf
.line(self.row_start + usize::from(self.cursor_row) - 1),
size.ws_col - 2,
)?;
self.cursor_row -= 1;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
@@ -335,7 +334,7 @@ impl State {
}
let size = term::size()?;
self.line_offset = line_offset(
- stdin,
+ &mut self.stdin,
&self
.buf
.line(self.row_start + usize::from(self.cursor_row) - 1),
@@ -343,15 +342,16 @@ impl State {
)?;
self.row_start -= 1;
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::Right => {
let size = term::size()?;
let line = self.line();
if self.cursor_col < size.ws_col - 2 && self.line_offset < line.len() {
- self.line_offset = ceil_grapheme_cluster(stdin, &line, self.line_offset + 1)?;
- self.update_cursor_col(stdin)?;
+ self.line_offset =
+ ceil_grapheme_cluster(&mut self.stdin, &line, self.line_offset + 1)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
@@ -381,8 +381,9 @@ impl State {
}
Key::End => {
let size = term::size()?;
- self.line_offset = line_offset(stdin, &self.line(), size.ws_col - 2)?;
- self.update_cursor_col(stdin)?;
+ let line = self.line();
+ self.line_offset = line_offset(&mut self.stdin, &line, size.ws_col - 2)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::PgUp => {
@@ -400,13 +401,13 @@ impl State {
start = self.row_start + 2 - usize::from(size.ws_row);
};
self.line_offset = line_offset(
- stdin,
+ &mut self.stdin,
&self.buf.line(start + usize::from(self.cursor_row)),
self.cursor_col,
)?;
self.row_start = start;
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::PgDn => {
@@ -415,7 +416,7 @@ impl State {
self.cursor_row = u16::try_from(self.buf.lines() - self.row_start)
.expect("self.buf.lines() - self.row_start < size.ws_row");
self.line_offset = self.line().len();
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
@@ -425,23 +426,26 @@ impl State {
}
if start + usize::from(self.cursor_row) <= self.buf.lines() {
self.line_offset = line_offset(
- stdin,
+ &mut self.stdin,
&self.buf.line(start + usize::from(self.cursor_row)),
self.cursor_col,
)?;
self.row_start = start;
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
- self.line_offset =
- line_offset(stdin, &self.buf.line(self.buf.lines()), self.cursor_col)?;
+ self.line_offset = line_offset(
+ &mut self.stdin,
+ &self.buf.line(self.buf.lines()),
+ self.cursor_col,
+ )?;
self.row_start = start;
self.cursor_row = u16::try_from(self.buf.lines() - start)
.expect("start + self.cursor_row > self.buf.lines()");
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::Byte(CTRL_Q) => {
@@ -463,7 +467,7 @@ impl State {
}
self.load();
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::Byte(CTRL_Y) => {
@@ -476,7 +480,7 @@ impl State {
self.n = Some(n + 1);
self.load();
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::Byte(BACKSPACE) | Key::Byte(OTHER_BACKSPACE) => {
@@ -500,21 +504,21 @@ impl State {
.slice(0, offset - 1)
.concat(&self.buf.slice(offset, self.buf.len()));
self.repaint_screen()?;
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
return Ok(true);
}
let line = self.line();
- self.line_offset = match floor_grapheme_cluster(stdin, &line, self.line_offset - 1)
- {
- Ok(x) => x,
- Err(_) => line.floor_char_boundary(self.line_offset - 1),
- };
+ self.line_offset =
+ match floor_grapheme_cluster(&mut self.stdin, &line, self.line_offset - 1) {
+ Ok(x) => x,
+ Err(_) => line.floor_char_boundary(self.line_offset - 1),
+ };
self.buf = self
.buf
.slice(0, line_start + self.line_offset)
.concat(&self.buf.slice(offset, self.buf.len()));
- self.update_cursor_col(stdin)?;
+ self.update_cursor_col()?;
stdout().flush()?;
}
Key::Byte(b'\r') | Key::Byte(b'\n') => {
@@ -575,7 +579,6 @@ fn edit(file: &OsStr) -> Result<(), String> {
return Err(format!("enter raw mode: {}", err));
}
};
- let mut stdin = BufReader::new(std::io::stdin());
let mut state = State {
history: vec![Snapshot {
buf: r.clone(),
@@ -592,6 +595,7 @@ fn edit(file: &OsStr) -> Result<(), String> {
cursor_col: 0,
line_offset: 0,
+ stdin: Reader::new(std::io::stdin()),
path: file,
inserting: false,
need_repaint_line: false,
@@ -607,10 +611,10 @@ fn edit(file: &OsStr) -> Result<(), String> {
return Err(format!("cannot write to screen: {}", err));
}
loop {
- let Ok(key) = term::read_key(&mut stdin) else {
+ let Ok(key) = state.stdin.read_key() else {
return Err(String::from("no input"));
};
- let Ok(ok) = state.keypress(&mut stdin, key) else {
+ let Ok(ok) = state.keypress(key) else {
state.need_repaint_screen = true;
let _ = stdout().flush();
continue;